Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [PATCH v2 00/47] A new target to debug Intel GPUs
@ 2024-12-13 15:59 Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 01/47] gdb, intelgt: add intelgt as a basic machine Tankut Baris Aktemur
                   ` (47 more replies)
  0 siblings, 48 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

Hello,

We (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

The v1 of the submission is available at

  https://sourceware.org/pipermail/gdb-patches/2024-July/210264.html

This revision (v2) makes the following changes:

  - The comments that have been received so far are addressed.

  - Patches are rebased on the master branch.

  - A new patch is added:
    "gdbserver: allow configuring for a heterogeneous target"

  - The patch titled "gdb: load solibs even when exec_bfd does not
    exist" has been revised and is now titled "gdb: load solibs if the
    target does not have the notion of an exec file".

  - The testsuite now includes a proc named 'sycl_with_intelgt_lock'
    similar to 'with_rocm_gpu_lock'.

GPU threads operate in a SIMD (single instruction multiple data)
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 a BoF hosted by
Intel and AMD in GNU Tools Cauldron 2024 for more details if
interested.

We provide a gdbserver low target definition.  The target uses the
Level-Zero debug API:

  https://spec.oneapi.io/level-zero/latest/tools/PROG.html#program-debug
  https://spec.oneapi.io/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:

  https://lists.freedesktop.org/archives/intel-xe/2024-December/061476.html (v3)
  https://lists.freedesktop.org/archives/intel-xe/2024-October/052260.html (v2)
  https://lists.freedesktop.org/archives/intel-xe/2024-July/043605.html (v1)

For Level-Zero based devices, we model hardware threads.  There is one
GDB thread for each hardware thread on the device.  We opted for this
model for the following reasons:

  - Programs that use GPUs to accelerate computation typically offload
    many computation kernels.  Hence, software threads in GPUs have
    much shorter lives than threads in multi-threaded CPU programs.
    For real-world cases, the data processed by the GPU is typically
    large, causing the number of software threads to be usually higher
    than the number of available hardware threads.  Therefore, dealing
    with software threads may cause proliferation of threads.
    Modeling hardware threads, on the other hand, means that they
    would be created once at the beginning of the debug session and
    then the list of threads stays stable.

  - As of today, Intel GPUs do not switch context for threads.  That
    is, once a software thread is assigned to run on a particular
    hardware thread, it always runs on that hardware thread until
    termination.  Therefore, focusing on a hardware thread does not
    create context switch confusion for the user that would otherwise
    be experienced with e.g. CPU threads.

Hardware threads may be idle inbetween computation kernel executions
or when a kernel does not utilize the GPU fully.  They may also be
used by applications other than the one currently under debug.  During
these times, those hardware threads cannot be interacted with
(e.g. cannot be interrupted) by the current debug user and appear as
unavailable.  To handle this case, we introduce an UNAVAILABLE wait
kind and also model it as a thread execution state.  In particular,
UNAVAILABLE means that we have tried to stop the thread and failed.

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'd welcome interested parties to watch the recording.

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 remote | gdbserver-intelgt --attach - 16458
  Remote debugging using | gdbserver-intelgt --attach - 16458
  Attached; given pid = 16458, updated to 1
  Remote debugging using stdio
  <unavailable> in ?? ()
  (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 upstreamed) that bring
(1) SIMD 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.

Best regards,
Baris

---
Albertano Caruso (2):
      gdb, intelgt: add disassemble feature for the Intel GT architecture.
      testsuite, arch, intelgt: add a disassembly test

Klaus Gerlicher (1):
      gdb, ze: on a whole process stop, mark all threads as not_resumed

Markus Metzger (15):
      gdb, arch, intelgt: add intelgt arch definitions
      gdbsupport, filestuff, ze: temporary files
      gdb, gdbserver, ze: in-memory libraries
      gdb, gdbserver, rsp, ze: acknowledge libraries
      gdb, solib, ze: solib_bfd_open_from_target_memory
      gdb, remote, ze: fix "$Hc-1#09...Packet received: E01" during startup
      gdb, infrun, ze: allow saving process events
      gdb, ze: add TARGET_WAITKIND_UNAVAILABLE
      gdb, infrun, ze: handle stopping unavailable threads
      gdb, infrun, ze: allow resuming unavailable threads
      gdb, gdbserver, ze: add U stop reply
      gdb, gdbserver, ze: add library notification to U stop reply
      gdbserver: wait for stopped threads in queue_stop_reply_callback
      gdb, dwarf, ze: add DW_OP_INTEL_regval_bits
      gdbserver, ze, intelgt: introduce ze-low and intel-ze-low targets

Natalia Saiapova (2):
      bfd: add intelgt target to BFD
      gdb: do not create a thread after a process event.

Nils-Christian Kempke (1):
      gdb, gdbserver, gdbsupport: add 'device' tag to XML target description

Tankut Baris Aktemur (26):
      gdb, intelgt: add intelgt as a basic machine
      ld: add intelgt as a target configuration
      opcodes: add intelgt as a configuration
      gdb, intelgt: add the target-dependent definitions for the Intel GT architecture
      gdbserver, ze: report TARGET_WAITKIND_UNAVAILABLE events
      gdb, ze: handle TARGET_WAITKIND_UNAVAILABLE in stop_all_threads
      gdb, remote: handle thread unavailability in print_one_stopped_thread
      gdb, remote: do 'remote_add_inferior' in 'remote_notice_new_inferior' earlier
      gdb, remote: handle a generic process PID in remote_notice_new_inferior
      gdb, remote: handle a generic process PID in process_stop_reply
      gdb: use the pid from inferior in setup_inferior
      gdb: revise the pid_to_exec_file target op
      gdb: load solibs if the target does not have the notion of an exec file
      gdbserver: import AC_LIB_HAVE_LINKFLAGS macro into the autoconf script
      gdbserver: add a pointer to the owner thread in regcache
      gdbserver: dump 'xx...x' in collect_register_as_string for unavailable register
      gdbserver: adjust pid after the target attaches
      gdbserver: allow configuring for a heterogeneous target
      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 and accessing data elements
      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
      testsuite, sycl: test canceling a stepping flow

 bfd/Makefile.am                                 |    2 +
 bfd/Makefile.in                                 |    4 +
 bfd/archures.c                                  |    4 +
 bfd/bfd-in2.h                                   |    6 +
 bfd/config.bfd                                  |   13 +-
 bfd/configure                                   |    1 +
 bfd/configure.ac                                |    1 +
 bfd/cpu-intelgt.c                               |   57 +
 bfd/elf64-intelgt.c                             |  195 ++
 bfd/libbfd.h                                    |    2 +
 bfd/reloc.c                                     |    7 +
 bfd/targets.c                                   |    2 +
 binutils/dwarf.c                                |    6 +
 binutils/readelf.c                              |    9 +
 config.sub                                      |    5 +-
 gdb/Makefile.in                                 |    8 +-
 gdb/NEWS                                        |   27 +
 gdb/arch/intelgt.c                              |   77 +
 gdb/arch/intelgt.h                              |  175 ++
 gdb/config.in                                   |    3 +
 gdb/configure                                   |  537 +++-
 gdb/configure.ac                                |   40 +
 gdb/configure.tgt                               |    5 +
 gdb/disasm-selftests.c                          |    4 +
 gdb/doc/gdb.texinfo                             |  153 +-
 gdb/dwarf2/expr.c                               |   36 +
 gdb/dwarf2/expr.h                               |    5 +
 gdb/dwarf2/loc.c                                |    2 +
 gdb/exec.c                                      |    6 +
 gdb/features/gdb-target.dtd                     |   19 +-
 gdb/features/library-list.dtd                   |   12 +-
 gdb/fork-child.c                                |   10 +-
 gdb/gdbthread.h                                 |   12 +-
 gdb/infcmd.c                                    |   14 +-
 gdb/inferior.h                                  |    4 +
 gdb/infrun.c                                    |  124 +-
 gdb/intelgt-tdep.c                              | 1093 +++++++++
 gdb/nat/fork-inferior.c                         |   10 +
 gdb/regcache.c                                  |    6 +-
 gdb/remote.c                                    |  196 +-
 gdb/solib-target.c                              |  142 +-
 gdb/solib.c                                     |  103 +-
 gdb/solist.h                                    |   25 +-
 gdb/target-delegates-gen.c                      |   50 +
 gdb/target-descriptions.c                       |   19 +
 gdb/target.c                                    |   16 +
 gdb/target.h                                    |   24 +
 gdb/target/waitstatus.c                         |    1 +
 gdb/target/waitstatus.h                         |   22 +
 gdb/testsuite/README                            |    9 +
 gdb/testsuite/boards/intel-offload.exp          |   36 +
 gdb/testsuite/gdb.arch/intelgt-disassemble.exp  |   82 +
 gdb/testsuite/gdb.arch/intelgt-program-bp.exp   |   83 +
 gdb/testsuite/gdb.arch/sycl-simple.cpp          |   42 +
 gdb/testsuite/gdb.sycl/break.exp                |   63 +
 gdb/testsuite/gdb.sycl/break2.exp               |   66 +
 gdb/testsuite/gdb.sycl/call-stack.cpp           |   92 +
 gdb/testsuite/gdb.sycl/call-stack.exp           |  179 ++
 gdb/testsuite/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      |   55 +
 gdb/testsuite/gdb.sycl/parallel-for-2D.cpp      |   73 +
 gdb/testsuite/gdb.sycl/parallel-for-2D.exp      |   55 +
 gdb/testsuite/gdb.sycl/scheduler-locking.exp    |   67 +
 gdb/testsuite/gdb.sycl/single-task.cpp          |   50 +
 gdb/testsuite/gdb.sycl/step-canceled.exp        |   86 +
 gdb/testsuite/gdb.sycl/step-into-function.exp   |   47 +
 gdb/testsuite/gdb.sycl/step-parallel-for.exp    |   63 +
 gdb/testsuite/gdb.sycl/step.exp                 |   51 +
 gdb/testsuite/gdb.threads/killed-outside.exp    |    4 +
 gdb/testsuite/lib/gdb.exp                       |   17 +-
 gdb/testsuite/lib/intelgt-utils.exp             |   43 +
 gdb/testsuite/lib/sycl-devices.cpp              |  107 +
 gdb/testsuite/lib/sycl-hello.cpp                |   43 +
 gdb/testsuite/lib/sycl-util.cpp                 |  135 +
 gdb/testsuite/lib/sycl.exp                      |  410 ++++
 gdb/thread.c                                    |    2 +-
 gdb/top.c                                       |   10 +
 gdb/xml-tdesc.c                                 |   76 +
 gdbserver/Makefile.in                           |    4 +-
 gdbserver/acinclude.m4                          |    5 +
 gdbserver/config.in                             |    6 +
 gdbserver/configure                             |  500 ++++
 gdbserver/configure.ac                          |   18 +
 gdbserver/configure.srv                         |   15 +-
 gdbserver/dll.cc                                |  155 +-
 gdbserver/dll.h                                 |   36 +-
 gdbserver/gdbthread.h                           |    2 +-
 gdbserver/intelgt-ze-low.cc                     | 1041 ++++++++
 gdbserver/linux-low.cc                          |    6 +-
 gdbserver/linux-low.h                           |    2 +-
 gdbserver/netbsd-low.cc                         |    2 +-
 gdbserver/netbsd-low.h                          |    2 +-
 gdbserver/regcache.cc                           |   26 +-
 gdbserver/regcache.h                            |    3 +
 gdbserver/remote-utils.cc                       |   21 +
 gdbserver/server.cc                             |  254 +-
 gdbserver/server.h                              |    7 +
 gdbserver/target.cc                             |   14 +
 gdbserver/target.h                              |   31 +-
 gdbserver/tdesc.cc                              |   16 +
 gdbserver/tdesc.h                               |    3 +
 gdbserver/win32-low.cc                          |    4 +-
 gdbserver/win32-low.h                           |    2 +-
 gdbserver/ze-low.cc                             | 2995 +++++++++++++++++++++++
 gdbserver/ze-low.h                              |  492 ++++
 gdbsupport/filestuff.cc                         |   53 +
 gdbsupport/filestuff.h                          |    6 +
 gdbsupport/tdesc.cc                             |   40 +
 gdbsupport/tdesc.h                              |   90 +
 include/dwarf2.def                              |    4 +
 include/elf/intelgt.h                           |   39 +
 ld/configure.tgt                                |    2 +
 opcodes/configure                               |    1 +
 opcodes/configure.ac                            |    1 +
 115 files changed, 11245 insertions(+), 143 deletions(-)
---
base-commit: 038590b067bafb19ceec62f73fe0b41ec37e8236
change-id: 20241213-upstream-intelgt-mvp-684d5f2f6730

Best regards,
-- 
Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 01/47] gdb, intelgt: add intelgt as a basic machine
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-16  7:53   ` Jan Beulich
  2024-12-13 15:59 ` [PATCH v2 02/47] bfd: add intelgt target to BFD Tankut Baris Aktemur
                   ` (46 subsequent siblings)
  47 siblings, 1 reply; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger, config-patches, binutils

Add 'intelgt' as a basic machine to config.sub.

To: <config-patches@gnu.org>
To: <binutils@sourceware.org>
---
 config.sub | 1 +
 1 file changed, 1 insertion(+)

diff --git a/config.sub b/config.sub
index 2c6a07ab3c34eabed8318ec0a37c0cc23b77a63f..63ff958ec125e543674e9b261d5e5bb2fa749c4e 100755
--- a/config.sub
+++ b/config.sub
@@ -1205,6 +1205,7 @@ case $cpu-$vendor in
 			| hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
 			| hexagon \
 			| i370 | i*86 | i860 | i960 | ia16 | ia64 \
+			| intelgt \
 			| ip2k | iq2000 \
 			| k1om \
 			| kvx \

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 02/47] bfd: add intelgt target to BFD
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 01/47] gdb, intelgt: add intelgt as a basic machine Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 03/47] ld: add intelgt as a target configuration Tankut Baris Aktemur
                   ` (45 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger, binutils

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         |   6 ++
 bfd/config.bfd        |  13 +++-
 bfd/configure         |   1 +
 bfd/configure.ac      |   1 +
 bfd/cpu-intelgt.c     |  57 +++++++++++++++
 bfd/elf64-intelgt.c   | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++
 bfd/libbfd.h          |   2 +
 bfd/reloc.c           |   7 ++
 bfd/targets.c         |   2 +
 binutils/readelf.c    |   9 +++
 include/elf/intelgt.h |  39 ++++++++++
 14 files changed, 339 insertions(+), 3 deletions(-)

diff --git a/bfd/Makefile.am b/bfd/Makefile.am
index dadbd0f788266dd8a95e814ec9f79e0aed3d5d63..1280962af633af3585ffd5bd826e290ae9c42b1b 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 a781e2b8959d9f36f8b0eb261bc0920e5f6f5832..9d7c57add1253a3e42b7bb044097bd46e2ff38dd 100644
--- a/bfd/Makefile.in
+++ b/bfd/Makefile.in
@@ -585,6 +585,7 @@ ALL_MACHINES = \
 	cpu-i386.lo \
 	cpu-ia64.lo \
 	cpu-iamcu.lo \
+	cpu-intelgt.lo \
 	cpu-ip2k.lo \
 	cpu-iq2000.lo \
 	cpu-kvx.lo \
@@ -668,6 +669,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 \
@@ -1024,6 +1026,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 \
@@ -1495,6 +1498,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 5c104af6ef35ae7bf0209c5597f12b44fdf6bda4..8778a8c3a02d49200ef56b65fb466cb4d843fb5e 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
@@ -650,6 +652,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;
@@ -738,6 +741,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 092a6587c1c443e0de29e0ea712436e052d4ee92..48bd8ae521c1bc9220f2211ac4153ffcc505dc4a 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -1665,6 +1665,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
@@ -7461,6 +7463,10 @@ enum bfd_reloc_code_real
   BFD_RELOC_LARCH_TLS_LD_PCREL20_S2,
   BFD_RELOC_LARCH_TLS_GD_PCREL20_S2,
   BFD_RELOC_LARCH_TLS_DESC_PCREL20_S2,
+
+  /* IntelGT relocations.  */
+  BFD_RELOC_ZE_SYM_ADDR32_HI,
+  BFD_RELOC_ZE_PER_THREAD_PAYLOAD_OFFSET_32,
   BFD_RELOC_UNUSED
 };
 typedef enum bfd_reloc_code_real bfd_reloc_code_real_type;
diff --git a/bfd/config.bfd b/bfd/config.bfd
old mode 100644
new mode 100755
index f8482d717e192880c925ad252ae176bfb706bd95..6a2d6945f57d2fee6487684270e9886b2d40d23a
--- a/bfd/config.bfd
+++ b/bfd/config.bfd
@@ -197,6 +197,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 ;;
 kvx)		 targ_archs=bfd_kvx_arch ;;
 loongarch*)	 targ_archs=bfd_loongarch_arch ;;
 m6811*|m68hc11*) targ_archs="bfd_m68hc11_arch bfd_m68hc12_arch bfd_m9s12x_arch bfd_m9s12xg_arch" ;;
@@ -712,12 +713,12 @@ case "${targ}" in
     ;;
   x86_64-*-linux-*)
     targ_defvec=x86_64_elf64_vec
-    targ_selvecs="i386_elf32_vec iamcu_elf32_vec x86_64_elf32_vec i386_pei_vec x86_64_pe_vec x86_64_pei_vec"
+    targ_selvecs="i386_elf32_vec iamcu_elf32_vec x86_64_elf32_vec i386_pei_vec x86_64_pe_vec x86_64_pei_vec intelgt_elf64_vec"
     want64=true
     ;;
   x86_64-*-mingw* | x86_64-*-pe | x86_64-*-pep | x86_64-*-cygwin)
     targ_defvec=x86_64_pe_vec
-    targ_selvecs="x86_64_pe_vec x86_64_pei_vec x86_64_pe_big_vec x86_64_elf64_vec i386_pe_vec i386_pei_vec i386_elf32_vec iamcu_elf32_vec pdb_vec"
+    targ_selvecs="x86_64_pe_vec x86_64_pei_vec x86_64_pe_big_vec x86_64_elf64_vec i386_pe_vec i386_pei_vec i386_elf32_vec iamcu_elf32_vec pdb_vec intelgt_elf64_vec intelgt_legacy_elf64_vec"
     want64=true
     targ_underscore=no
     ;;
@@ -788,7 +789,13 @@ case "${targ}" in
     targ_defvec=i386_elf32_vec
     targ_selvecs="i386_msdos_vec i386_aout_vec"
     ;;
-
+#ifdef BFD64
+  intelgt-*-elf)
+    targ_defvec=intelgt_elf64_vec
+    targ_selvecs="intelgt_elf64_vec"
+    want64=true
+    ;;
+#endif
   ip2k-*-elf)
     targ_defvec=ip2k_elf32_vec
     targ_underscore=yes
diff --git a/bfd/configure b/bfd/configure
index 2db9f76b9eb39eb7e86619b11c9dafa224deda5e..146c24e3660558856b166d926e152a9c535ec506 100755
--- a/bfd/configure
+++ b/bfd/configure
@@ -15908,6 +15908,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 465a7463d4818e045a3c33105d6e1c16e556b90b..816547a8f279ce37774c08daf317fe53cb909bad 100644
--- a/bfd/configure.ac
+++ b/bfd/configure.ac
@@ -494,6 +494,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 0000000000000000000000000000000000000000..dd78e7260e7d8814a7fa4f5a0fb3f453006371a6
--- /dev/null
+++ b/bfd/cpu-intelgt.c
@@ -0,0 +1,57 @@
+/* BFD support for the Intel(R) Graphics Technology architecture.
+   Copyright (C) 2019-2024 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"
+
+static void *
+bfd_arch_intelgt_fill (bfd_size_type count,
+		       bool is_bigendian ATTRIBUTE_UNUSED,
+		       bool code)
+{
+  void *fill = bfd_malloc (count);
+
+  if (fill != NULL)
+    {
+      /* nop on gen is 0x7e.  */
+      memset (fill, code ? 0x7e : 0, count);
+    }
+
+  return fill;
+}
+
+const bfd_arch_info_type bfd_intelgt_arch =
+  {
+    64, /* 64 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_intelgt_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 0000000000000000000000000000000000000000..50ccbac05067933c545d302632f8812364df056a
--- /dev/null
+++ b/bfd/elf64-intelgt.c
@@ -0,0 +1,195 @@
+/* Intel(R) Graphics Technology-specific support for ELF
+   Copyright (C) 2022-2024 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"
+
+#define MINUS_ONE (~ (bfd_vma) 0)
+
+#define INTELGT_ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
+
+static bool
+elf64_intelgt_elf_object_p (bfd *abfd)
+{
+  return bfd_default_set_arch_mach (abfd, bfd_arch_intelgt, bfd_mach_intelgt);
+}
+
+/* Map BFD relocs to the IntelGT relocs.  */
+struct elf_reloc_map
+{
+  bfd_reloc_code_real_type bfd_reloc_val;
+  unsigned char elf_reloc_val;
+};
+
+static const struct elf_reloc_map elf64_intelgt_reloc_map[] =
+{
+  { BFD_RELOC_64, R_ZE_SYM_ADDR },
+  { BFD_RELOC_32, R_ZE_SYM_ADDR_32 },
+  { BFD_RELOC_ZE_SYM_ADDR32_HI, R_ZE_SYM_ADDR32_HI },
+  { BFD_RELOC_ZE_PER_THREAD_PAYLOAD_OFFSET_32,
+    R_PER_THREAD_PAYLOAD_OFFSET_32 },
+};
+
+static reloc_howto_type elf64_intelgt_howto_table[] =
+{
+  HOWTO (R_ZE_NONE,		/* type */
+	 0,			/* rightshift */
+	 0,			/* size (0 = byte, 1 = short, 2 = long) */
+	 0,			/* bitsize */
+	 false,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont,/* complain_on_overflow */
+	 NULL,			/* special_function */
+	 "R_ZE_NONE",		/* name */
+	 false,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0,			/* dst_mask */
+	 false),		/* pcrel_offset */
+  HOWTO (R_ZE_SYM_ADDR,		/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 64,			/* bitsize */
+	 false,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_unsigned,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc, /* special_function */
+	 "R_ZE_SYM_ADDR",	/* name */
+	 false,			/* partial_inplace */
+	 MINUS_ONE,		/* src_mask */
+	 MINUS_ONE,		/* dst_mask */
+	 false),		/* pcrel_offset */
+  HOWTO (R_ZE_SYM_ADDR_32,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 false,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_unsigned,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc, /* special_function */
+	 "R_ZE_SYM_ADDR_32",	/* name */
+	 false,			/* partial_inplace */
+	 MINUS_ONE,		/* src_mask */
+	 MINUS_ONE,		/* dst_mask */
+	 false),		/* pcrel_offset */
+  HOWTO (R_ZE_SYM_ADDR32_HI,	/* type */
+	 32,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 false,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_unsigned,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc, /* special_function */
+	 "R_ZE_SYM_ADDR32_HI",	/* name */
+	 false,			/* partial_inplace */
+	 MINUS_ONE,		/* src_mask */
+	 MINUS_ONE,		/* dst_mask */
+	 false),		/* pcrel_offset */
+  HOWTO (R_PER_THREAD_PAYLOAD_OFFSET_32,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 false,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_unsigned,	/* complain_on_overflow */
+	 NULL,			/* special_function */
+	 "R_PER_THREAD_PAYLOAD_OFFSET_32",	/* name */
+	 false,			/* partial_inplace */
+	 MINUS_ONE,		/* src_mask */
+	 MINUS_ONE,		/* dst_mask */
+	 false),		/* pcrel_offset */
+};
+
+/* Given a BFD reloc type, return a HOWTO structure.  */
+static reloc_howto_type *
+elf64_intelgt_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
+				 bfd_reloc_code_real_type code)
+{
+  unsigned int i;
+
+  for (i = 0; i < INTELGT_ARRAY_SIZE (elf64_intelgt_reloc_map); i++)
+  {
+    struct elf_reloc_map reloc_map  = elf64_intelgt_reloc_map[i];
+
+    if (reloc_map.bfd_reloc_val == code)
+      return &elf64_intelgt_howto_table[reloc_map.elf_reloc_val];
+  }
+
+  return NULL;
+}
+
+/* Given relocation NAME, find its HOWTO structure.  */
+static reloc_howto_type *
+elf64_intelgt_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
+				 const char *r_name)
+{
+  unsigned int i;
+
+  for (i = 0; i < INTELGT_ARRAY_SIZE (elf64_intelgt_howto_table); i++)
+    if (elf64_intelgt_howto_table[i].name != NULL
+	&& strcasecmp (elf64_intelgt_howto_table[i].name, r_name) == 0)
+      return &elf64_intelgt_howto_table[i];
+
+  return NULL;
+}
+
+/* Sets HOWTO of the BFD_RELOC to the entry of howto table based
+   on the type of ELF_RELOC.  */
+static bool
+elf64_info_to_howto (bfd *abfd, arelent *bfd_reloc,
+		     Elf_Internal_Rela *elf_reloc)
+{
+  unsigned int r_type = ELF32_R_TYPE (elf_reloc->r_info);
+  bfd_reloc->howto = &elf64_intelgt_howto_table[r_type];
+
+  if (bfd_reloc->howto == NULL)
+    {
+      /* xgettext:c-format */
+      _bfd_error_handler (_("%pB: unsupported relocation type %#x"),
+			  abfd, r_type);
+      return false;
+    }
+  return true;
+}
+
+#define ELF_MAXPAGESIZE		    0x40000000
+
+#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 elf64_bed			    elf64_intelgt_bed
+
+#define elf_backend_object_p		    elf64_intelgt_elf_object_p
+
+#define elf_backend_want_plt_sym	    0
+
+#define bfd_elf64_bfd_reloc_type_lookup     elf64_intelgt_reloc_type_lookup
+#define bfd_elf64_bfd_reloc_name_lookup     elf64_intelgt_reloc_name_lookup
+#define elf_info_to_howto		    elf64_info_to_howto
+
+#include "elf64-target.h"
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index 7d7ae1eaec9c7e9d38a56142495669d630db712b..02c7b23ca2e3886d0633090d793d3cc5b12f2c28 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -3626,6 +3626,8 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_LARCH_TLS_LD_PCREL20_S2",
   "BFD_RELOC_LARCH_TLS_GD_PCREL20_S2",
   "BFD_RELOC_LARCH_TLS_DESC_PCREL20_S2",
+  "BFD_RELOC_ZE_SYM_ADDR32_HI",
+  "BFD_RELOC_ZE_PER_THREAD_PAYLOAD_OFFSET_32",
  "@@overflow: BFD_RELOC_UNUSED@@",
 };
 #endif
diff --git a/bfd/reloc.c b/bfd/reloc.c
index f60776299c863f2ed88a9262bf990aa979ee3cb5..7593f59ddc8ba03f0b21a3e53b0d6ded0c71fc9c 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -8260,6 +8260,13 @@ ENUMX
 ENUMDOC
   LARCH relocations.
 
+ENUM
+  BFD_RELOC_ZE_SYM_ADDR32_HI
+ENUMX
+  BFD_RELOC_ZE_PER_THREAD_PAYLOAD_OFFSET_32
+ENUMDOC
+  IntelGT relocations.
+
 ENDSENUM
   BFD_RELOC_UNUSED
 
diff --git a/bfd/targets.c b/bfd/targets.c
index 178e9d30a2b72dd184cf2d716e78f90944e188eb..3994a87601489bb932431000f51054550fa23f27 100644
--- a/bfd/targets.c
+++ b/bfd/targets.c
@@ -764,6 +764,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 64e461daf860515f293b821d50917aa0207544e1..755138ff180ca776620a0ad567ed14b6f55381e2 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -124,6 +124,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"
@@ -2373,6 +2374,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;
 	}
 
       if (rtype == NULL)
@@ -15547,6 +15552,8 @@ is_32bit_abs_reloc (Filedata * filedata, unsigned int reloc_type)
       return reloc_type == 1; /* R_XTENSA_32.  */
     case EM_Z80:
       return reloc_type == 6; /* R_Z80_32.  */
+    case EM_INTELGT:
+      return reloc_type == 2; /* R_ZE_SYM_ADDR_32 */
     default:
       {
 	static unsigned int prev_warn = 0;
@@ -15686,6 +15693,8 @@ is_64bit_abs_reloc (Filedata * filedata, unsigned int reloc_type)
       return reloc_type == 18;	/* R_MIPS_64.  */
     case EM_KVX:
       return reloc_type == 3; /* R_KVX_64 */
+    case EM_INTELGT:
+      return reloc_type == 1;	/* R_ZE_SYM_ADDR.  */
     default:
       return false;
     }
diff --git a/include/elf/intelgt.h b/include/elf/intelgt.h
new file mode 100644
index 0000000000000000000000000000000000000000..16fbf9ce4f77e51f1152d1d20fa276e2fc9a3446
--- /dev/null
+++ b/include/elf/intelgt.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2022-2024 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 __INTELGT_H_
+#define __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_PER_THREAD_PAYLOAD_OFFSET_32,      4)
+END_RELOC_NUMBERS (R_ZE_max)
+
+#endif /* __INTELGT_H_ */

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 03/47] ld: add intelgt as a target configuration
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 01/47] gdb, intelgt: add intelgt as a basic machine Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 02/47] bfd: add intelgt target to BFD Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-16  7:43   ` Jan Beulich
  2024-12-13 15:59 ` [PATCH v2 04/47] opcodes: add intelgt as a configuration Tankut Baris Aktemur
                   ` (44 subsequent siblings)
  47 siblings, 1 reply; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger, binutils

Add 'intelgt-*-elf' as a target configuration for ld.

To: <binutils@sourceware.org>
---
 ld/configure.tgt | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/ld/configure.tgt b/ld/configure.tgt
index b5f272e5ba28eed7ddaf0039cd5f916199c597e7..e4394828ad2387f147ba79155f1623edf691f00b 100644
--- a/ld/configure.tgt
+++ b/ld/configure.tgt
@@ -468,6 +468,8 @@ ia64-*-*vms*)		targ_emul=elf64_ia64_vms
 			;;
 ia64-*-aix*)		targ_emul=elf64_aix
 			;;
+intelgt-*-elf)		targ_emul=elf_x86_64
+			;;
 ip2k-*-elf)		targ_emul=elf32ip2k
 			;;
 iq2000-*-elf)		targ_emul=elf32iq2000

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 04/47] opcodes: add intelgt as a configuration
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (2 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 03/47] ld: add intelgt as a target configuration Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-16  7:44   ` Jan Beulich
  2024-12-13 15:59 ` [PATCH v2 05/47] gdb, arch, intelgt: add intelgt arch definitions Tankut Baris Aktemur
                   ` (43 subsequent siblings)
  47 siblings, 1 reply; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger, binutils

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 547e5afc76657db80c08ee762d19fd8ff17fb4e0..262fdb25e3f8e17134a48f21cf62dbe856145b81 100755
--- a/opcodes/configure
+++ b/opcodes/configure
@@ -14470,6 +14470,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 51045b6217815b2c29d802b94c49a69c1719b791..c9f6cabb9220cf6073df3a3de6efe9b57b7ef347 100644
--- a/opcodes/configure.ac
+++ b/opcodes/configure.ac
@@ -286,6 +286,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.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 05/47] gdb, arch, intelgt: add intelgt arch definitions
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (3 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 04/47] opcodes: add intelgt as a configuration Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2025-07-08  3:03   ` Thiago Jung Bauermann
  2024-12-13 15:59 ` [PATCH v2 06/47] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture Tankut Baris Aktemur
                   ` (42 subsequent siblings)
  47 siblings, 1 reply; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

From: Markus Metzger <markus.t.metzger@intel.com>

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>
---
 gdb/Makefile.in    |   1 +
 gdb/arch/intelgt.c |  77 +++++++++++++++++++++++
 gdb/arch/intelgt.h | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 253 insertions(+)

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 52950759711af139f18e963b13fef7e70a5fc6e0..82e035fa02a29ec073c3a909397aca75d82e4a89 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -751,6 +751,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 0000000000000000000000000000000000000000..89a19baea8428c1479d1e43de676a4f0dfb01952
--- /dev/null
+++ b/gdb/arch/intelgt.c
@@ -0,0 +1,77 @@
+/* Copyright (C) 2019-2024 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;
+}
+
+} /* namespace intelgt */
diff --git a/gdb/arch/intelgt.h b/gdb/arch/intelgt.h
new file mode 100644
index 0000000000000000000000000000000000000000..38e89053a9b22e26c370525758f8da7f3790f6bb
--- /dev/null
+++ b/gdb/arch/intelgt.h
@@ -0,0 +1,175 @@
+/* Copyright (C) 2019-2024 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 ARCH_INTELGT_H
+#define ARCH_INTELGT_H
+
+#include "gdbsupport/tdesc.h"
+#include <string>
+#include <vector>
+
+namespace intelgt {
+
+/* Various arch constants.  */
+
+enum breakpoint_kind
+{
+  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
+};
+
+/* Instruction details.  */
+
+enum
+{
+  /* The opcode mask for bits 6:0.  */
+  OPC_MASK = 0x7f,
+
+  /* Send instruction opcodes.  */
+  OPC_SEND = 0x31,
+  OPC_SENDC = 0x32,
+};
+
+/* Selected instruction control bit positions.  */
+
+enum
+{
+  /* The End Of Thread control.  Only used for SEND and SENDC.  */
+  CTRL_EOT = 34,
+};
+
+/* 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 bool
+is_compacted_inst (gdb::array_view<const gdb_byte> inst)
+{
+  /* Check the CmptCtrl flag (bit 29).  */
+  return inst[3] & 0x20;
+}
+
+static inline int
+breakpoint_bit_offset (gdb::array_view<const gdb_byte> inst)
+{
+  return (is_compacted_inst (inst) ? 7 : 30);
+}
+
+static inline bool
+set_breakpoint (gdb::array_view<gdb_byte> inst)
+{
+  return set_inst_bit (inst, breakpoint_bit_offset (inst));
+}
+
+static inline bool
+clear_breakpoint (gdb::array_view<gdb_byte> inst)
+{
+  return clear_inst_bit (inst, breakpoint_bit_offset (inst));
+}
+
+static inline bool
+has_breakpoint (gdb::array_view<const gdb_byte> inst)
+{
+  return get_inst_bit (inst, breakpoint_bit_offset (inst));
+}
+
+static inline unsigned int
+inst_length_compacted ()
+{
+  return COMPACT_INST_LENGTH;
+}
+
+static inline unsigned int
+inst_length_full ()
+{
+  return MAX_INST_LENGTH;
+}
+
+static inline unsigned int
+inst_length (gdb::array_view<const gdb_byte> inst)
+{
+  return (is_compacted_inst (inst)
+	  ? inst_length_compacted ()
+	  : inst_length_full ());
+}
+
+} /* namespace intelgt */
+
+#endif

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 06/47] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (4 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 05/47] gdb, arch, intelgt: add intelgt arch definitions Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2025-07-08  2:43   ` Thiago Jung Bauermann
  2024-12-13 15:59 ` [PATCH v2 07/47] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description Tankut Baris Aktemur
                   ` (41 subsequent siblings)
  47 siblings, 1 reply; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

Introduce gdb/intelgt-tdep.c for the Intel GT target.  The target is
defined in a future patch as a gdbserver low target implementation.

Other than, 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>
---
 gdb/Makefile.in    |   1 +
 gdb/configure.tgt  |   5 +
 gdb/intelgt-tdep.c | 980 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/regcache.c     |   6 +-
 4 files changed, 991 insertions(+), 1 deletion(-)

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 82e035fa02a29ec073c3a909397aca75d82e4a89..05280c7787e467b2cb7b063d893b1c394b506c12 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -757,6 +757,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 f7b9e32cba97671a2b2d59c054eb2ff96ef919de..a090fe78c5ee53f3b10798dae9e0b44f339b4fbb 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -352,6 +352,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 100755
index 0000000000000000000000000000000000000000..57c359bf355c5771db38b8d213f6681a043c2b33
--- /dev/null
+++ b/gdb/intelgt-tdep.c
@@ -0,0 +1,980 @@
+/* Target-dependent code for the Intel(R) Graphics Technology architecture.
+
+   Copyright (C) 2019-2024 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 "frame-unwind.h"
+#include "gdbsupport/gdb_obstack.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.  */
+
+struct regnum_range
+{
+  int start;
+  int end;
+};
+
+/* The encoding for XE version enumerates follows this pattern, which is
+   aligned with the IGA encoding.  */
+
+#define XE_VERSION(MAJ, MIN) (((MAJ) << 24) | (MIN))
+
+/* Supported GDB GEN platforms.  */
+
+enum xe_version
+{
+  XE_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),
+};
+
+/* Helper functions to request and translate the device id/version.  */
+
+[[maybe_unused]] static xe_version get_xe_version (unsigned int device_id);
+
+/* Data specific for this architecture.  */
+
+struct intelgt_gdbarch_tdep : gdbarch_tdep_base
+{
+  /* $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;
+
+  /* Cached $framedesc pseudo-register type.  */
+  type *framedesc_type = nullptr;
+
+  /* Initialize ranges to -1 as "not-yet-set" indicator.  */
+  intelgt_gdbarch_tdep ()
+  {
+    memset (&regset_ranges, -1, sizeof regset_ranges);
+  }
+
+  /* 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;
+  }
+};
+
+/* The 'register_type' gdbarch method.  */
+
+static type *
+intelgt_register_type (gdbarch *gdbarch, int regno)
+{
+  type *typ = tdesc_register_type (gdbarch, regno);
+  return typ;
+}
+
+/* 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[%zu:%zu] is outside the range of %s[%d:0]."),
+	   regname, (offset + buffer.size () - 1), offset,
+	   regname, (regsize - 1));
+
+  register_status reg_status
+    = regcache->cooked_read_part (regnum, offset, buffer);
+
+  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);
+}
+
+static int
+intelgt_pseudo_register_num (gdbarch *arch, const char *name);
+
+/* 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",
+    "scrbase",
+    "genstbase",
+    "sustbase",
+    "blsustbase",
+    "blsastbase",
+    "scrbase2"
+  };
+
+  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;
+	    const char* name = sba_dwarf_reg_order [sba_num];
+
+	    return user_reg_map_name_to_regnum (gdbarch, name, -1);
+	  }
+	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 std::max (start_pc, 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.");
+}
+
+/* Callback function to unwind the $framedesc register.  */
+
+static value *
+intelgt_dwarf2_prev_framedesc (const frame_info_ptr &this_frame,
+			       void **this_cache, int regnum)
+{
+  gdbarch *gdbarch = get_frame_arch (this_frame);
+  intelgt_gdbarch_tdep *data
+    = gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch);
+
+  int actual_regnum = data->framedesc_base_regnum ();
+
+  /* Unwind the actual GRF register.  */
+  return frame_unwind_register_value (this_frame, actual_regnum);
+}
+
+/* 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");
+  int framedesc_regnum = intelgt_pseudo_register_num (gdbarch, "framedesc");
+
+  if (regnum == ip_regnum)
+    reg->how = DWARF2_FRAME_REG_RA;
+  else if (regnum == gdbarch_sp_regnum (gdbarch))
+    reg->how = DWARF2_FRAME_REG_CFA;
+  /* We use special functions to unwind the $framedesc register.  */
+  else if (regnum == framedesc_regnum)
+    {
+      reg->how = DWARF2_FRAME_REG_FN;
+      reg->loc.fn = intelgt_dwarf2_prev_framedesc;
+    }
+}
+
+/* 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.  Read directly from the
+     regcache instead of unwinding, as the frame unwind info may
+     simply be unavailable.  The isabase register does not change
+     during kernel execution, so this must be safe.  */
+  regcache *regcache = get_thread_regcache (inferior_thread ());
+  CORE_ADDR isabase = intelgt_get_isabase (regcache);
+
+  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;
+}
+
+static const struct frame_unwind intelgt_unwinder =
+  {
+    "intelgt prologue",
+    NORMAL_FRAME,			/* type */
+    default_frame_unwind_stop_reason,	/* stop_reason */
+    intelgt_frame_this_id,		/* this_id */
+    nullptr,				/* prev_register */
+    nullptr,				/* unwind_data */
+    default_frame_sniffer,		/* sniffer */
+    nullptr,				/* dealloc_cache */
+  };
+
+
+/* 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 I think 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;
+  bp->shadow_len = intelgt::inst_length (inst);
+
+  /* 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);
+  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));
+
+  /* Warn if we're inserting a permanent breakpoint.  */
+  if (intelgt::has_breakpoint (bp->shadow_contents))
+    warning (_("Re-inserting permanent breakpoint at %s."),
+	     paddress (gdbarch, bp->placed_address));
+
+  /* See comment in mem-break.c on write_inferior_memory.  */
+  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 I think 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;
+    }
+
+  const bool is_bkpt = intelgt::has_breakpoint (inst);
+
+  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;
+}
+
+/* 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 ());
+}
+
+/* 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;
+  regcache->cooked_write_part (data->cr0_regnum, sizeof (uint32_t) * 2,
+			       gdb::make_array_view ((gdb_byte *) &ip,
+						     sizeof (uint32_t)));
+}
+
+/* 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);
+  intelgt_gdbarch_tdep *data
+    = gdbarch_tdep<intelgt_gdbarch_tdep> (arch);
+
+  if (strcmp (name, "framedesc") == 0)
+    {
+      if (data->framedesc_type != nullptr)
+	return data->framedesc_type;
+      type *frame = arch_composite_type (arch, "frame_desc", TYPE_CODE_STRUCT);
+      append_composite_type_field (frame, "return_ip", bt->builtin_uint32);
+      append_composite_type_field (frame, "return_callmask",
+				   bt->builtin_uint32);
+      append_composite_type_field (frame, "be_sp", bt->builtin_uint32);
+      append_composite_type_field (frame, "be_fp", bt->builtin_uint32);
+      append_composite_type_field (frame, "fe_fp", bt->builtin_uint64);
+      append_composite_type_field (frame, "fe_sp", bt->builtin_uint64);
+      data->framedesc_type = frame;
+      return frame;
+    }
+  else 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, "framedesc") == 0)
+    {
+      int grf_num = data->framedesc_base_regnum ();
+      return pseudo_from_raw_part (next_frame, pseudo_regnum, grf_num, 0);
+    }
+  else 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, "framedesc") == 0)
+    {
+      int grf_num = data->framedesc_base_regnum ();
+      int grf_size = register_size (arch, grf_num);
+      int desc_size = register_size (arch, pseudo_regnum);
+      gdb_assert (grf_size >= desc_size);
+      pseudo_to_raw_part (next_frame, buf, grf_num, 0);
+    }
+  else 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;
+}
+
+/* Helper function to translate the device id to a device version.  */
+
+static xe_version
+get_xe_version (unsigned int device_id)
+{
+  xe_version device_xe_version = XE_INVALID;
+  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:
+	device_xe_version = XE_HPG;
+	break;
+
+      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:
+	device_xe_version = XE_HP;
+	break;
+
+      case 0x0BD0:
+      case 0x0BD4:
+      case 0x0BD5:
+      case 0x0BD6:
+      case 0x0BD7:
+      case 0x0BD8:
+      case 0x0BD9:
+      case 0x0BDA:
+      case 0x0BDB:
+      case 0x0B69:
+      case 0x0B6E:
+	device_xe_version = XE_HPC;
+	break;
+
+      case 0x6420:
+      case 0x64A0:
+      case 0x64B0:
+      case 0xE202:
+      case 0xE20B:
+      case 0xE20C:
+      case 0xE20D:
+      case 0xE212:
+	device_xe_version = XE2;
+	break;
+    }
+
+  return device_xe_version;
+}
+
+/* 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 *gdbarch
+    = gdbarch_alloc (&info,
+		     gdbarch_tdep_up (new intelgt_gdbarch_tdep));
+  intelgt_gdbarch_tdep *data
+    = gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch);
+
+  /* Initialize register info.  */
+  set_gdbarch_num_regs (gdbarch, 0);
+  set_gdbarch_register_name (gdbarch, tdesc_register_name);
+
+  if (tdesc_has_registers (tdesc))
+    {
+      tdesc_arch_data_up tdesc_data = tdesc_data_alloc ();
+
+      /* First assign register numbers to all registers.  The
+	 callback function will record any relevant metadata
+	 about it in the intelgt_gdbarch_data instance to be
+	 inspected after.  */
+
+      tdesc_use_registers (gdbarch, tdesc, std::move (tdesc_data),
+			   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 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");
+
+      /* Unconditionally enabled pseudo-registers:  */
+      data->enabled_pseudo_regs.push_back ("ip");
+      data->enabled_pseudo_regs.push_back ("framedesc");
+
+      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_ptr_bit (gdbarch, 64);
+  set_gdbarch_addr_bit (gdbarch, 64);
+  set_gdbarch_long_bit (gdbarch, 64);
+
+  set_gdbarch_register_type (gdbarch, intelgt_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_unwinder);
+
+  set_gdbarch_return_value_as_value (gdbarch, intelgt_return_value_as_value);
+
+  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;
+}
+
+/* Dump the target specific data for this architecture.  */
+
+static void
+intelgt_dump_tdep (gdbarch *gdbarch, ui_file *file)
+{
+  /* Implement target-specific print output if and
+     when gdbarch_tdep is defined for this architecture.  */
+}
+
+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);
+}
+
+void _initialize_intelgt_tdep ();
+void
+_initialize_intelgt_tdep ()
+{
+  gdbarch_register (bfd_arch_intelgt, intelgt_gdbarch_init,
+		    intelgt_dump_tdep);
+
+  /* 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/regcache.c b/gdb/regcache.c
index f2c403b88d0c58352d0e11d3e2c2c92ce2b0c40d..04d19ad43dd0bc716ca6306ccfe4be6d8afc498d 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -1926,9 +1926,13 @@ selftest_skiparch (struct gdbarch *gdbarch)
        Stack backtrace will not work.
      We could instead capture the output and then filter out the warning, but
      that seems more trouble than it's worth.  */
+  /* The 'intelgt' arch populates the register sets dynamically based on
+     what the target reports.  With no target description, the register
+     file is empty.  */
   return (strcmp (name, "m68hc11") == 0
 	  || strcmp (name, "m68hc12") == 0
-	  || strcmp (name, "m68hc12:HCS12") == 0);
+	  || strcmp (name, "m68hc12:HCS12") == 0
+	  || strcmp (name, "intelgt") == 0);
 }
 
 /* Test regcache::cooked_read gets registers from raw registers and

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 07/47] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (5 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 06/47] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 16:45   ` Eli Zaretskii
  2025-07-08  4:04   ` Thiago Jung Bauermann
  2024-12-13 15:59 ` [PATCH v2 08/47] gdb, intelgt: add disassemble feature for the Intel GT architecture Tankut Baris Aktemur
                   ` (40 subsequent siblings)
  47 siblings, 2 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

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         | 25 +++++++++++++
 gdb/features/gdb-target.dtd | 19 +++++++++-
 gdb/target-descriptions.c   | 19 ++++++++++
 gdb/xml-tdesc.c             | 76 ++++++++++++++++++++++++++++++++++++++
 gdbserver/tdesc.cc          | 16 ++++++++
 gdbserver/tdesc.h           |  3 ++
 gdbsupport/tdesc.cc         | 40 ++++++++++++++++++++
 gdbsupport/tdesc.h          | 90 +++++++++++++++++++++++++++++++++++++++++++++
 9 files changed, 295 insertions(+), 1 deletion(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index 361d7726ba0f7065b1ad78c34aa0decb860f4e9c..8f8aa1462f5f757bda3d25ca60e2eba80660bcb6 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -174,6 +174,14 @@ vFile:stat
   vFile:fstat but takes a filename rather than an open file
   descriptor.
 
+* 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 15
 
 * The MPX commands "show/set mpx bound" have been deprecated, as Intel
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 85ac3d9aab63d790b66aae8bfe38156b44f4e8fc..ddd414659fcc554813fc2a12a79e581ad7a188b9 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -48552,6 +48552,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
@@ -48649,6 +48650,30 @@ 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"
+          family="abc"
+          model="xyz"
+          stepping="3"
+          name="Intel(R) Arc(TM) A750 Graphics"
+          pci-slot="03:00.0"
+          uuid="0003000856a18086"
+          total-cores="448"
+          total-threads="3584"
+          subdevice-id="1"/>
+@end smallexample
+
 @subsection Features
 @cindex <feature>
 
diff --git a/gdb/features/gdb-target.dtd b/gdb/features/gdb-target.dtd
index d07703fca8b6b22bffb8acea99b40ce7f7a590c8..4afaffc6d0c228ee14f2e46492b152245dea3990 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,21 @@
 
 <!ELEMENT compatible	(#PCDATA)>
 
+<!ELEMENT device	EMPTY>
+<!ATTLIST device
+	vendor-id	CDATA	#IMPLIED
+	target-id	CDATA	#IMPLIED
+	family		CDATA	#IMPLIED
+	model		CDATA	#IMPLIED
+	stepping	CDATA	#IMPLIED
+	name		CDATA	#IMPLIED
+	pci-slot	CDATA	#IMPLIED
+	uuid		CDATA	#IMPLIED
+	total-cores	CDATA	#IMPLIED
+	total-threads	CDATA	#IMPLIED
+	subdevice-id	CDATA	#IMPLIED
+	>
+
 <!ELEMENT feature
 	((vector | flags | struct | union )*, reg*)>
 <!ATTLIST feature
diff --git a/gdb/target-descriptions.c b/gdb/target-descriptions.c
index 3ee8a751f3b303b7d02df40b3b3836c8a8c83066..34a00265b4bb601995baffcd0d99ce26af4264cc 100644
--- a/gdb/target-descriptions.c
+++ b/gdb/target-descriptions.c
@@ -358,6 +358,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;
 
@@ -643,6 +646,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 436c493d4f914eb71a80bcbc08dc2ddd38c72027..61cba42e9eee2ec4cc66e14a120c5bef909e48b0 100644
--- a/gdb/xml-tdesc.c
+++ b/gdb/xml-tdesc.c
@@ -137,6 +137,65 @@ tdesc_end_compatible (struct gdb_xml_parser *parser,
   tdesc_add_compatible (data->tdesc, arch);
 }
 
+/* Handle the end 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, "family");
+  if (attr != nullptr)
+    device->family = (char *) attr->value.get ();
+
+  attr = xml_find_attribute (attributes, "model");
+  if (attr != nullptr)
+    device->model = (char *) attr->value.get ();
+
+  attr = xml_find_attribute (attributes, "stepping");
+  if (attr != nullptr)
+    device->stepping = * (ULONGEST *) attr->value.get ();
+
+  attr = xml_find_attribute (attributes, "name");
+  if (attr != nullptr)
+    device->name = (char *) attr->value.get ();
+
+  attr = xml_find_attribute (attributes, "pci-slot");
+  if (attr != nullptr)
+    device->pci_slot = (char *) attr->value.get ();
+
+  attr = xml_find_attribute (attributes, "uuid");
+  if (attr != nullptr)
+    device->uuid = (char *) attr->value.get ();
+
+  attr = xml_find_attribute (attributes, "total-cores");
+  if (attr != nullptr)
+    device->total_cores = * (ULONGEST *) attr->value.get ();
+
+  attr = xml_find_attribute (attributes, "total-threads");
+  if (attr != nullptr)
+    device->total_threads = * (ULONGEST *) attr->value.get ();
+
+  attr = xml_find_attribute (attributes, "subdevice-id");
+  if (attr != nullptr)
+    device->subdevice_id = * (ULONGEST *) attr->value.get ();
+
+  set_tdesc_device_info (data->tdesc, device);
+}
+
 /* Handle the start of a <target> element.  */
 
 static void
@@ -588,6 +647,21 @@ 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 },
+  { "family", GDB_XML_AF_OPTIONAL, NULL, NULL },
+  { "model", GDB_XML_AF_OPTIONAL, NULL, NULL },
+  { "stepping", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+  { "name", GDB_XML_AF_OPTIONAL, NULL, NULL },
+  { "pci-slot", GDB_XML_AF_OPTIONAL, NULL, NULL },
+  { "uuid", GDB_XML_AF_OPTIONAL, NULL, NULL },
+  { "total-cores", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+  { "total-threads", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+  { "subdevice-id", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, 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 }
@@ -600,6 +674,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 da1287abbbed5933bf8ceddd4aa5d7a70ad4d473..dc89ac1cd4f3a189b2e3ba30998119e8e4df4531 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 9264786de51173c262aadcaa31d205f0b274b7c4..3eb6d67db02f7f36b197d499f456d9cabeffa833 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 080d39c485dc5d9d7ced21e067bec5391b132c94..ae7a7b36c7ca2d265c5707437239801599e7fb62 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,44 @@ 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);
+
+  string_appendf (tmp, " family=\"%s\"", device->family.c_str ());
+  string_appendf (tmp, " model=\"%s\"", device->model.c_str ());
+
+  if (device->stepping.has_value ())
+    string_appendf (tmp, " stepping=\"%d\"", *device->stepping);
+
+  string_appendf (tmp, " name=\"%s\"", device->name.c_str ());
+  string_appendf (tmp, " pci-slot=\"%s\"", device->pci_slot.c_str ());
+  string_appendf (tmp, " uuid=\"%s\"", device->uuid.c_str ());
+
+  if (device->total_cores.has_value ())
+    string_appendf (tmp, " total-cores=\"%ld\"", *device->total_cores);
+
+  if (device->total_threads.has_value ())
+    string_appendf (tmp, " total-threads=\"%ld\"", *device->total_threads);
+
+  if (device->subdevice_id.has_value ())
+    string_appendf (tmp, " subdevice-id=\"%d\"", *device->subdevice_id);
+
+  string_appendf (tmp, "/>");
+  add_line (tmp);
+}
+
 /* See gdbsupport/tdesc.h.  */
 
 void
diff --git a/gdbsupport/tdesc.h b/gdbsupport/tdesc.h
index c9e7603369cbf986187908c2d1f8fbe35a0dc02d..29de4d231f16667c8297bcbe403f683c30a28762 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,84 @@ struct tdesc_feature : tdesc_element
 
 typedef std::unique_ptr<tdesc_feature> tdesc_feature_up;
 
+/* The device information in a target description.  */
+
+struct tdesc_device : tdesc_element
+{
+  tdesc_device ()
+  {
+    family = "-";
+    model = "-";
+    name = "-";
+    pci_slot = "-";
+    uuid = "-";
+  }
+
+  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 family of the device.  */
+  std::string family;
+
+  /* The model of the device.  */
+  std::string model;
+
+  /* The stepping of the device.  */
+  std::optional<uint32_t> stepping;
+
+  /* The name of the device.  */
+  std::string name;
+
+  /* The location where the device is positioned.  */
+  std::string pci_slot;
+
+  /* The UUID of the device.  */
+  std::string uuid;
+
+  /* The number of cores available in the device.  */
+  std::optional<size_t> total_cores;
+
+  /* The number of threads available in the device.  */
+  std::optional<size_t> total_threads;
+
+  /* The subdevice id, if this device is a subdevice.  */
+  std::optional<uint32_t> subdevice_id;
+
+  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
+	    && family == other.family
+	    && model == other.model
+	    && stepping == other.stepping
+	    && name == other.name
+	    && pci_slot == other.pci_slot
+	    && uuid == other.uuid
+	    && total_cores == other.total_cores
+	    && total_threads == other.total_threads
+	    && subdevice_id == other.subdevice_id);
+  }
+
+  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
@@ -347,6 +429,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,
@@ -439,6 +528,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.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 08/47] gdb, intelgt: add disassemble feature for the Intel GT architecture.
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (6 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 07/47] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2025-07-09  3:12   ` Thiago Jung Bauermann
  2024-12-13 15:59 ` [PATCH v2 09/47] gdbsupport, filestuff, ze: temporary files Tankut Baris Aktemur
                   ` (39 subsequent siblings)
  47 siblings, 1 reply; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

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/config.in          |   3 +
 gdb/configure          | 537 ++++++++++++++++++++++++++++++++++++++++++++++++-
 gdb/configure.ac       |  40 ++++
 gdb/disasm-selftests.c |   4 +
 gdb/intelgt-tdep.c     | 115 ++++++++++-
 gdb/top.c              |  10 +
 7 files changed, 711 insertions(+), 4 deletions(-)

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 05280c7787e467b2cb7b063d893b1c394b506c12..96b3abf9cd76db3bc99d9b4b7944d51f0811a8e7 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -250,6 +250,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
@@ -680,7 +684,7 @@ CLIBS = $(SIM) $(READLINE) $(OPCODES) $(LIBCTF) $(BFD) $(ZLIB) $(ZSTD_LIBS) \
 	$(LIBEXPAT) $(LIBLZMA) $(LIBBABELTRACE) $(LIBIPT) \
 	$(WIN32LIBS) $(LIBGNU) $(LIBGNU_EXTRA_LIBS) $(LIBICONV) \
 	$(GMPLIBS) $(SRCHIGH_LIBS) $(LIBXXHASH) $(PTHREAD_LIBS) \
-	$(DEBUGINFOD_LIBS) $(LIBBABELTRACE_LIB)
+	$(DEBUGINFOD_LIBS) $(LIBBABELTRACE_LIB) $(LIBIGA)
 CDEPS = $(NAT_CDEPS) $(SIM) $(BFD) $(READLINE_DEPS) $(CTF_DEPS) \
 	$(OPCODES) $(INTL_DEPS) $(LIBIBERTY) $(CONFIG_DEPS) $(LIBGNU) \
 	$(LIBSUPPORT)
diff --git a/gdb/config.in b/gdb/config.in
index db63aeaec75a69573d21ec5da102eb422e58e9e8..c42755234dc7a12e5f942b2ee2d37d8190ebb8a2 100644
--- a/gdb/config.in
+++ b/gdb/config.in
@@ -271,6 +271,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 de750f4fafee64900fc1a6d4a1d8aed2ead73657..f2be390c7d792dd4bfde45337b51f93f072aeb41 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
@@ -956,6 +959,8 @@ with_python
 with_python_libdir
 with_guile
 enable_source_highlight
+with_libiga64_prefix
+with_libiga64_type
 with_sysroot
 with_system_gdbinit
 with_system_gdbinit_dir
@@ -1728,6 +1733,9 @@ 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-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
@@ -11499,7 +11507,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11502 "configure"
+#line 11510 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -11605,7 +11613,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11608 "configure"
+#line 11616 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -29071,6 +29079,531 @@ fi
 
 
 
+# Check for Intel(R) Graphics Technology assembler library
+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
+
+
+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
+
+case "${target}" in
+  intelgt-*)
+    intelgt_target=true
+    ;;
+esac
+
+if test x${intelgt_target} = xtrue; then
+  if test "$HAVE_LIBIGA64" != yes; then
+    { $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
+else
+  # Do not link libiga64 spuriously
+  HAVE_LIBIGA64=no
+  LIBIGA64=
+  LTLIBIGA64=
+fi
+
 # ------------------------- #
 # Checks for header files.  #
 # ------------------------- #
diff --git a/gdb/configure.ac b/gdb/configure.ac
index 230c0be79c7c51f64f748cfa5c446fc0d62b7520..c2b6fad04876a578a4ea28a6a6f30bf5e5d8686f 100644
--- a/gdb/configure.ac
+++ b/gdb/configure.ac
@@ -1289,6 +1289,46 @@ fi
 AC_SUBST(SRCHIGH_LIBS)
 AC_SUBST(SRCHIGH_CFLAGS)
 
+# Check for Intel(R) Graphics Technology assembler library
+AC_LANG_PUSH([C++])
+AC_LIB_HAVE_LINKFLAGS([iga64], [],
+  [#include "assert.h"
+   #include "iga/iga.h"],
+  [iga_version_string();])
+AC_LANG_POP([C++])
+
+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
+
+case "${target}" in
+  intelgt-*)
+    intelgt_target=true
+    ;;
+esac
+
+if test x${intelgt_target} = xtrue; then
+  if test "$HAVE_LIBIGA64" != yes; then
+    AC_MSG_WARN([libiga64 is missing or unusable; some features may be unavailable.])
+  fi
+else
+  # Do not link libiga64 spuriously
+  HAVE_LIBIGA64=no
+  LIBIGA64=
+  LTLIBIGA64=
+fi
+
 # ------------------------- #
 # Checks for header files.  #
 # ------------------------- #
diff --git a/gdb/disasm-selftests.c b/gdb/disasm-selftests.c
index 221b94f085135837510fa9062082702c76c5c802..941c09b84f10703ed932919ea2790b466833ffe8 100644
--- a/gdb/disasm-selftests.c
+++ b/gdb/disasm-selftests.c
@@ -115,6 +115,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);
diff --git a/gdb/intelgt-tdep.c b/gdb/intelgt-tdep.c
index 57c359bf355c5771db38b8d213f6681a043c2b33..7d0ae38480fe89b145c1c29be0e58c96480e3335 100755
--- 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;
@@ -119,6 +123,11 @@ struct intelgt_gdbarch_tdep : gdbarch_tdep_base
     gdb_assert (regset_ranges[intelgt::REGSET_GRF].end > 1);
     return regset_ranges[intelgt::REGSET_GRF].end - 1;
   }
+
+#if defined (HAVE_LIBIGA64)
+  /* libiga context for disassembly.  */
+  iga_context_t iga_ctx = nullptr;
+#endif
 };
 
 /* The 'register_type' gdbarch method.  */
@@ -511,13 +520,77 @@ 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_pc_function (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.  */
+  unsigned int full_length = intelgt::inst_length_full ();
+  unsigned int compact_length = intelgt::inst_length_compacted ();
+
+  std::unique_ptr<bfd_byte[]> insn (new bfd_byte[full_length]);
+
+  int status = (*info->read_memory_func) (memaddr, insn.get (),
+					  compact_length, info);
+  if (status != 0)
+    {
+      /* Aborts disassembling with a memory_error exception.  */
+      (*info->memory_error_func) (status, memaddr, info);
+      return -1;
+    }
+  if (!intelgt::is_compacted_inst (gdb::make_array_view (insn.get (),
+							 compact_length)))
+    {
+      status = (*info->read_memory_func) (memaddr, insn.get (),
+					  full_length, info);
+      if (status != 0)
+	{
+	  /* Aborts disassembling with a memory_error exception.  */
+	  (*info->memory_error_func) (status, memaddr, info);
+	  return -1;
+	}
+    }
+
+#if defined (HAVE_LIBIGA64)
+  char *dbuf;
+  iga_disassemble_options_t dopts = IGA_DISASSEMBLE_OPTIONS_INIT ();
+  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;
+  iga_status_t iga_status
+    = iga_context_disassemble_instruction (iga_ctx, &dopts, insn.get (),
+					   intelgt_disasm_sym_cb,
+					   info, &dbuf);
+  if (iga_status != IGA_SUCCESS)
+    return -1;
+
+  (*info->fprintf_func) (info->stream, "%s", dbuf);
+
+  if (intelgt::is_compacted_inst (gdb::make_array_view (insn.get (),
+							full_length)))
+    return compact_length;
+  else
+    return full_length;
+#else
+  gdb_printf (_("\nDisassemble feature not available: libiga64 "
+		"is missing.\n"));
   return -1;
+#endif /* defined (HAVE_LIBIGA64)  */
 }
 
 /* Utility function to look up the pseudo-register number by name.  Exact
@@ -864,6 +937,46 @@ 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."));
+	  gdbarch_free (gdbarch);
+	  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);
+	  gdbarch_free (gdbarch);
+	  return nullptr;
+	}
+      else
+	{
+	  iga_version = (iga_gen_t) 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);
+  iga_context_create (&options, &data->iga_ctx);
+#endif
+
   /* Initialize register info.  */
   set_gdbarch_num_regs (gdbarch, 0);
   set_gdbarch_register_name (gdbarch, tdesc_register_name);
diff --git a/gdb/top.c b/gdb/top.c
index d750f3305f2eef7c5a30cef7f647537916a80499..c27f7d60254a4d384dfd770ce98eb4f0923dee49 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -1523,6 +1523,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.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 09/47] gdbsupport, filestuff, ze: temporary files
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (7 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 08/47] gdb, intelgt: add disassemble feature for the Intel GT architecture Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2025-07-14  1:26   ` Thiago Jung Bauermann
  2024-12-13 15:59 ` [PATCH v2 10/47] gdb, gdbserver, ze: in-memory libraries Tankut Baris Aktemur
                   ` (38 subsequent siblings)
  47 siblings, 1 reply; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

From: Markus Metzger <markus.t.metzger@intel.com>

Add gdb_create_tmpfile to create a temporary file based on a name template
and store the filename to be unlinked when GDB (or gdbserver) exits.
---
 gdbsupport/filestuff.cc | 53 +++++++++++++++++++++++++++++++++++++++++++++++++
 gdbsupport/filestuff.h  |  6 ++++++
 2 files changed, 59 insertions(+)

diff --git a/gdbsupport/filestuff.cc b/gdbsupport/filestuff.cc
index 9e07af28dc2db5975776763376d567c51de6376c..151905fc05ca5c4d6636e17b8344fcf77fd93e6b 100644
--- a/gdbsupport/filestuff.cc
+++ b/gdbsupport/filestuff.cc
@@ -17,11 +17,14 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "filestuff.h"
+#include "pathstuff.h"
+#include "scoped_fd.h"
 #include <fcntl.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <algorithm>
+#include <list>
 
 #ifdef USE_WIN32API
 #include <winsock2.h>
@@ -544,3 +547,53 @@ read_text_file_to_string (const char *path)
 
   return read_remainder_of_file (file.get ());
 }
+
+/* A class to keep temporary files until GDB exits (normally).  */
+
+struct tmpfilekeeper_s
+{
+  std::list<std::string> files;
+
+  tmpfilekeeper_s () = default;
+  tmpfilekeeper_s (const tmpfilekeeper_s &) = delete;
+  tmpfilekeeper_s (tmpfilekeeper_s &&) = delete;
+
+  ~tmpfilekeeper_s ()
+  {
+    for (const std::string &file : files)
+      {
+	const char *cfile = file.c_str ();
+	int status = unlink (cfile);
+	if (status != 0)
+	  warning (_("failed to remove temporary file %s: %s"), cfile,
+		   safe_strerror (errno));
+      }
+  }
+
+  tmpfilekeeper_s &operator= (const tmpfilekeeper_s &) = delete;
+  tmpfilekeeper_s &operator= (tmpfilekeeper_s &&) = delete;
+};
+static tmpfilekeeper_s tmpfilekeeper;
+
+/* See gdbsupport/filestuff.h.  */
+
+gdb_file_up
+gdb_create_tmpfile (std::string &base, int flags)
+{
+  std::string absbase { get_standard_temp_dir () + "/" + base };
+  gdb::char_vector name { make_temp_filename (absbase) };
+
+  scoped_fd fd { gdb_mkostemp_cloexec (name.data (), O_BINARY) };
+  if (fd.get () == -1)
+    error (_("failed to create temporary file %s: %s"), name.data (),
+	   safe_strerror (errno));
+
+  gdb_file_up file { fd.to_file ("wb") };
+  if (file.get () == nullptr)
+    error (_("failed to open %s: %s"), name.data (), safe_strerror (errno));
+
+  base = name.data ();
+  tmpfilekeeper.files.emplace_back (base);
+
+  return file;
+}
diff --git a/gdbsupport/filestuff.h b/gdbsupport/filestuff.h
index e2ee141d46f01bc0888a871830a5559419b63f03..6b7d1ecf9354d21e186c6f6b7aceb3aa591c4465 100644
--- a/gdbsupport/filestuff.h
+++ b/gdbsupport/filestuff.h
@@ -71,6 +71,12 @@ gdb_open_cloexec (const std::string &filename, int flags,
   return gdb_open_cloexec (filename.c_str (), flags, mode);
 }
 
+/* Create a temporary file that will automatically get deleted when GDB
+   terminates (normally).  NAME needs to be a template filename and will
+   be modified to contain the actual template filename on return.  */
+
+extern gdb_file_up gdb_create_tmpfile (std::string &name, int flags = 0);
+
 /* Like 'fopen', but ensures that the returned file descriptor has the
    close-on-exec flag set.  */
 

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 10/47] gdb, gdbserver, ze: in-memory libraries
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (8 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 09/47] gdbsupport, filestuff, ze: temporary files Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2025-07-14  2:35   ` Thiago Jung Bauermann
  2025-07-16  4:08   ` Thiago Jung Bauermann
  2024-12-13 15:59 ` [PATCH v2 11/47] gdb, gdbserver, rsp, ze: acknowledge libraries Tankut Baris Aktemur
                   ` (37 subsequent siblings)
  47 siblings, 2 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

From: Markus Metzger <markus.t.metzger@intel.com>

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                      |   6 +++
 gdb/doc/gdb.texinfo           |  40 +++++++++++-----
 gdb/features/library-list.dtd |   8 +++-
 gdb/remote.c                  |   3 ++
 gdb/solib-target.c            |  85 +++++++++++++++++++++++++++++++--
 gdb/solib.c                   |  27 +++++++++--
 gdb/solist.h                  |  13 +++++-
 gdbserver/dll.cc              |  59 +++++++++++++++++++++++
 gdbserver/dll.h               |  19 +++++++-
 gdbserver/server.cc           | 106 +++++++++++++++++++++++++++++++++++++++---
 gdbserver/server.h            |   3 ++
 11 files changed, 340 insertions(+), 29 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index 8f8aa1462f5f757bda3d25ca60e2eba80660bcb6..b60f66403116e73b4c5d2b2b4d847578da6e701a 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -174,6 +174,12 @@ vFile:stat
   vFile:fstat but takes a filename rather than an open file
   descriptor.
 
+qXfer:libraries:read's response
+
+  The qXfer:libraries:read query supports reporting in-memory libraries.  GDB
+  indicates support by supplying qXfer:libraries:read:in-memory-library+ in the
+  qSupported packet.
+
 * Changed remote packets
 
 qXfer:features:read:target.xml
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index ddd414659fcc554813fc2a12a79e581ad7a188b9..c84a8372c223e724e3042322a8bee07b12423050 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -48026,9 +48026,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
@@ -48040,6 +48041,10 @@ 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}.
 
+@value{GDBN} indicates support for in-memory library elements by
+supplying the @code{qXfer:libraries:read:in-memory-library+}
+@samp{qSupported} feature (@pxref{qSupported}).
+
 A simple memory map, with one loaded library relocated by a single
 offset, looks like this:
 
@@ -48051,6 +48056,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"/>
+  </library>
+</library-list>
+@end smallexample
+
 Another simple memory map, with one loaded library with three
 allocated sections (.text, .data, .bss), looks like this:
 
@@ -48068,14 +48083,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.1">
+<!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 98ed7bc28dcc4562ef4259fdd78138fe69d69d29..f55071c8e906f091752e8ca78ec29bcd76028433 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)*>
-<!ATTLIST library-list  version CDATA   #FIXED  "1.0">
+<!ELEMENT library-list  (library | in-memory-library)*>
+<!ATTLIST library-list  version CDATA   #FIXED  "1.1">
 
 <!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/remote.c b/gdb/remote.c
index f41129915683194237d1bba56d17df61ae89c063..7d074a5df322d68ded8f96c4832bc8c247435a4f 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -5956,6 +5956,9 @@ remote_target::remote_query_supported ()
 	  != AUTO_BOOLEAN_FALSE)
 	remote_query_supported_append (&q, "memory-tagging+");
 
+      remote_query_supported_append
+	(&q, "qXfer:libraries:read:in-memory-library+");
+
       /* Keep this one last to work around a gdbserver <= 7.10 bug in
 	 the qSupported:xmlRegisters=i386 handling.  */
       if (remote_support_xml != NULL
diff --git a/gdb/solib-target.c b/gdb/solib-target.c
index 8f73d5df55bddcc32708f7f96f35ae42e0730053..7165aed7e22f87c96a3716397e9380aff03a2a61 100644
--- a/gdb/solib-target.c
+++ b/gdb/solib-target.c
@@ -23,16 +23,36 @@
 #include "symfile.h"
 #include "target.h"
 #include "solib-target.h"
+#include "gdbsupport/filestuff.h"
+#include "gdb_bfd.h"
 #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;
+
   /* The library's name.  The name is normally kept in the struct
-     so_list; it is only here during XML parsing.  */
+     so_list; it is only here during XML parsing.
+
+     This is only valid if location == lm_on_disk.  */
   std::string name;
 
+  /* 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.  */
 
@@ -122,12 +142,32 @@ library_list_start_library (struct gdb_xml_parser *parser,
 {
   auto *list = (std::vector<lm_info_target_up> *) user_data;
   lm_info_target *item = new lm_info_target;
+  item->location = lm_on_disk;
   item->name
     = (const char *) xml_find_attribute (attributes, "name")->value.get ();
 
   list->emplace_back (item);
 }
 
+/* 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)
+{
+  auto *list = (std::vector<lm_info_target_up> *) user_data;
+  lm_info_target *item = new lm_info_target;
+  item->location = lm_in_memory;
+  item->begin = (CORE_ADDR) *(ULONGEST *)
+    xml_find_attribute (attributes, "begin")->value.get ();
+  item->end = (CORE_ADDR) *(ULONGEST *)
+    xml_find_attribute (attributes, "end")->value.get ();
+
+  list->emplace_back (item);
+}
+
 static void
 library_list_end_library (struct gdb_xml_parser *parser,
 			  const struct gdb_xml_element *element,
@@ -156,7 +196,7 @@ library_list_start_list (struct gdb_xml_parser *parser,
     {
       const char *string = (const char *) version->value.get ();
 
-      if (strcmp (string, "1.0") != 0)
+      if ((strcmp (string, "1.0") != 0) && (strcmp (string, "1.1") != 0))
 	gdb_xml_error (parser,
 		       _("Library list has unsupported version \"%s\""),
 		       string);
@@ -191,10 +231,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 }
 };
 
@@ -246,10 +295,35 @@ solib_target_current_sos (void)
   for (lm_info_target_up &info : library_list)
     {
       auto &new_solib = sos.emplace_back ();
+      switch (info->location)
+	{
+	case lm_on_disk:
+	  /* We don't need a copy of the name in INFO anymore.  */
+	  new_solib.so_name = std::move (info->name);
+	  new_solib.so_original_name = new_solib.so_name;
+	  break;
+
+	case lm_in_memory:
+	  {
+	    if (info->end <= info->begin)
+	      error (_("bad in-memory-library location: begin=%s, end=%s"),
+		     core_addr_to_string_nz (info->begin),
+		     core_addr_to_string_nz (info->end));
+
+	    /* Give it a name although this isn't really needed.  */
+	    std::string orig_name
+	      = std::string ("in-memory-")
+	      + core_addr_to_string_nz (info->begin)
+	      + "-"
+	      + core_addr_to_string_nz (info->end);
+
+	    new_solib.so_original_name = orig_name;
+	    new_solib.begin = info->begin;
+	    new_solib.end = info->end;
+	  }
+	  break;
+	}
 
-      /* We don't need a copy of the name in INFO anymore.  */
-      new_solib.so_name = std::move (info->name);
-      new_solib.so_original_name = new_solib.so_name;
       new_solib.lm_info = std::move (info);
     }
 
@@ -414,4 +488,5 @@ const solib_ops solib_target_so_ops =
   nullptr,
   nullptr,
   default_find_solib_addr,
+  gdb_bfd_open_from_target_memory,
 };
diff --git a/gdb/solib.c b/gdb/solib.c
index fdefdf0b1423032fa39f8ee9aae7f1c35f13e1d1..cb302f9215257ddff18c94f5be39e189b02d84e6 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -485,8 +485,29 @@ solib_map_sections (solib &so)
 {
   const solib_ops *ops = gdbarch_so_ops (current_inferior ()->arch ());
 
-  gdb::unique_xmalloc_ptr<char> filename (tilde_expand (so.so_name.c_str ()));
-  gdb_bfd_ref_ptr abfd (ops->bfd_open (filename.get ()));
+  gdb_bfd_ref_ptr abfd;
+  if (so.so_name[0] != '\0')
+    {
+      gdb::unique_xmalloc_ptr<char> filename
+	(tilde_expand (so.so_name.c_str ()));
+      abfd = ops->bfd_open (filename.get ());
+    }
+  else if (so.begin != 0 && so.end != 0)
+    {
+      if (ops->bfd_open_from_target_memory == nullptr)
+	error (_("Target does not support in-memory shared libraries."));
+
+      if (so.end <= so.begin)
+	error (_("Bad address range [%s; %s) for in-memory shared library."),
+	       core_addr_to_string_nz (so.begin),
+	       core_addr_to_string_nz (so.end));
+
+      abfd = ops->bfd_open_from_target_memory (so.begin,
+					       so.end - so.begin,
+					       gnutarget);
+    }
+  else
+    internal_error (_("bad so_list"));
 
   /* 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
@@ -533,7 +554,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.so_name.c_str ()));
 	      abfd = nullptr;
 	    }
 	}
diff --git a/gdb/solist.h b/gdb/solist.h
index 8ad0aefd9e32b264b08a853eb3577829a8e74767..c4d053a5c6c0261f1176121b2bc1f904ac2424a4 100644
--- a/gdb/solist.h
+++ b/gdb/solist.h
@@ -67,9 +67,14 @@ struct solib : intrusive_list_node<solib>
      map we've already loaded.  */
   std::string so_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 so_name;
 
+  /* The address range of an in-memory shared object.  Both BEGIN and END
+     are zero for on-disk shared objects.  */
+  CORE_ADDR begin, end;
+
   /* 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.
@@ -180,6 +185,12 @@ struct solib_ops
      name).  */
 
   std::optional<CORE_ADDR> (*find_solib_addr) (solib &so);
+
+  /* Open an in-memory shared library at ADDR of at most SIZE bytes.  The
+     TARGET string is used to identify the target.  */
+  gdb_bfd_ref_ptr (*bfd_open_from_target_memory) (CORE_ADDR addr,
+						  CORE_ADDR size,
+						  const char *target);
 };
 
 /* A unique pointer to a so_list.  */
diff --git a/gdbserver/dll.cc b/gdbserver/dll.cc
index f49aa560aec57e246948d944ec377b0beb8d1dc5..c2c63e9a69a54ddede77e40ea33b4e19cbdc71af 100644
--- a/gdbserver/dll.cc
+++ b/gdbserver/dll.cc
@@ -40,6 +40,17 @@ loaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
   proc->dlls_changed = true;
 }
 
+/* Record a newly loaded in-memory DLL at BASE_ADDR for PROC.  */
+
+void
+loaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
+	    CORE_ADDR base_addr)
+{
+  gdb_assert (proc != nullptr);
+  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 the current process.  */
 
@@ -58,6 +69,9 @@ unloaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
   gdb_assert (proc != nullptr);
   auto pred = [&] (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;
@@ -89,3 +103,48 @@ unloaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
       proc->dlls_changed = true;
     }
 }
+
+/* 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)
+{
+  gdb_assert (proc != nullptr);
+  auto pred = [&] (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 != UNSPECIFIED_CORE_ADDR
+	  && begin == dll.begin
+	  && (end == UNSPECIFIED_CORE_ADDR
+	      || end == dll.end))
+	return true;
+
+      return false;
+    };
+
+  auto iter = std::find_if (proc->all_dlls.begin (), proc->all_dlls.end (),
+			    pred);
+
+  if (iter == proc->all_dlls.end ())
+    /* For some inferiors we might get unloaded_dll events without having
+       a corresponding loaded_dll.  In that case, the dll cannot be found
+       in ALL_DLL, and there is nothing further for us to do.  */
+    return;
+  else
+    {
+      /* DLL has been found so remove the entry and free associated
+	 resources.  */
+      proc->all_dlls.erase (iter);
+      proc->dlls_changed = 1;
+    }
+}
diff --git a/gdbserver/dll.h b/gdbserver/dll.h
index e603df4be2b26017fa5b795f861d8d4111779d85..9bce28e70cf3939b5b062e4b9c0f0d2d10a1d1d7 100644
--- a/gdbserver/dll.h
+++ b/gdbserver/dll.h
@@ -24,19 +24,36 @@ 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_)
   {}
 
+  location_t location;
   std::string name;
+  CORE_ADDR begin;
+  CORE_ADDR end;
   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 264fac44b74db69746fdecb1cdd1fd748139cc4f..90902099a1b5a84fe86cb8391bf983a5943e78c5 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -1884,6 +1884,97 @@ handle_qxfer_features (const char *annex,
   return len;
 }
 
+static std::string
+dll_to_tmpfile (dll_info &dll)
+{
+  if (dll.end <= dll.begin)
+    error (_("bad in-memory-library location: begin=%s, end=%s"),
+	   core_addr_to_string_nz (dll.begin),
+	   core_addr_to_string_nz (dll.end));
+
+  gdb::byte_vector buffer (dll.end - dll.begin);
+  int errcode = gdb_read_memory (dll.begin, buffer.data (), buffer.size ());
+  if (errcode != 0)
+    error (_("failed to read in-memory library at %s..%s"),
+	   core_addr_to_string_nz (dll.begin),
+	   core_addr_to_string_nz (dll.end));
+
+  std::string name
+    = std::string ("gdb-in-memory-solib-")
+    + core_addr_to_string_nz (dll.begin)
+    + "-"
+    + core_addr_to_string_nz (dll.end);
+
+  gdb_file_up file = gdb_create_tmpfile (name);
+
+  size_t written = fwrite (buffer.data (), buffer.size (), 1, file.get ());
+  if (written != 1)
+    error (_("failed to write into %s"), name.c_str ());
+
+  return name;
+}
+
+/* Print a qXfer:libraries:read entry for DLL.  */
+
+static std::string
+print_qxfer_libraries_entry (dll_info &dll)
+{
+  switch (dll.location)
+    {
+    case dll_info::in_memory:
+      if (get_client_state ().in_memory_library_supported)
+	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));
+
+      /* GDB does not support in-memory-library.  Fall back to storing it in a
+	 temporary file and report that file to GDB.  */
+      dll.name = dll_to_tmpfile (dll);
+      [[fallthrough]];
+
+    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));
+    }
+
+  warning (_("unknown dll location: %x"), dll.location);
+  return std::string ();
+}
+
+/* Determine the library-list version required for communicating the shared
+   libraries.  */
+
+static std::string
+library_list_version_needed (const std::list<dll_info> &dlls)
+{
+  const client_state &cs = get_client_state ();
+  int major = 1, minor = 0;
+
+  for (const dll_info &dll : dlls)
+    {
+      switch (dll.location)
+	{
+	case dll_info::on_disk:
+	  major = std::max (major, 1);
+	  minor = std::max (minor, 0);
+	  break;
+
+	case dll_info::in_memory:
+	  if (cs.in_memory_library_supported)
+	    {
+	      major = std::max (major, 1);
+	      minor = std::max (minor, 1);
+	    }
+	  break;
+	}
+    }
+
+  return std::to_string (major) + std::string (".") + std::to_string (minor);
+}
+
 /* Handle qXfer:libraries:read.  */
 
 static int
@@ -1897,13 +1988,13 @@ handle_qxfer_libraries (const char *annex,
   if (annex[0] != '\0' || current_thread == NULL)
     return -1;
 
-  std::string document = "<library-list version=\"1.0\">\n";
-
   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));
+  std::string document = "<library-list version=\""
+    + library_list_version_needed (proc->all_dlls)
+    + "\">\n";
+
+  for (dll_info &dll : proc->all_dlls)
+    document += print_qxfer_libraries_entry (dll);
 
   document += "</library-list>\n";
 
@@ -2761,6 +2852,8 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 		}
 	      else if (feature == "error-message+")
 		cs.error_message_supported = true;
+	      else if (feature == "qXfer:libraries:read:in-memory-library+")
+		cs.in_memory_library_supported = true;
 	      else
 		{
 		  /* Move the unknown features all together.  */
@@ -2791,6 +2884,7 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 	  /* We do not have any hook to indicate whether the non-SVR4 target
 	     backend supports qXfer:libraries:read, so always report it.  */
 	  strcat (own_buf, ";qXfer:libraries:read+");
+	  strcat (own_buf, ";qXfer:libraries:read:in-memory-library+");
 	}
 
       if (the_target->supports_read_auxv ())
diff --git a/gdbserver/server.h b/gdbserver/server.h
index e1297d41f8406dab670397f5600e4e08558f23b7..3bacd1062f8bf47720189d2d02b36ddb6089e6bc 100644
--- a/gdbserver/server.h
+++ b/gdbserver/server.h
@@ -168,6 +168,9 @@ struct client_state
      space randomization feature before starting an inferior.  */
   int disable_randomization = 1;
 
+  /* True if qXfer:libraries:read supports in-memory-library.  */
+  bool in_memory_library_supported = false;
+
   int pass_signals[GDB_SIGNAL_LAST];
   int program_signals[GDB_SIGNAL_LAST];
   int program_signals_p = 0;

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 11/47] gdb, gdbserver, rsp, ze: acknowledge libraries
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (9 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 10/47] gdb, gdbserver, ze: in-memory libraries Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 16:43   ` Eli Zaretskii
  2025-07-16  4:20   ` Thiago Jung Bauermann
  2024-12-13 15:59 ` [PATCH v2 12/47] gdb, solib, ze: solib_bfd_open_from_target_memory Tankut Baris Aktemur
                   ` (36 subsequent siblings)
  47 siblings, 2 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

From: Markus Metzger <markus.t.metzger@intel.com>

On some accelerator devices, device shared libraries are loaded from a
host thread rather than from a device thread.  The reporting entity may
not be the one that actually does the load.

Intel GPU devices, for example, that are based on Level-Zero, will report
shared library events via the device's debug interface.  This is triggered
from a host thread calling the run-time interface for loading a device
shared library.

The Level-Zero run-time ensures that this host thread will not return
until the respective debug event has been acknowledged by the debugger.
This allows debuggers to set breakpoints before the new library is used.

Add a mechanism that allows gdbserver to request acknowledgement of newly
reported shared libraries and GDB to acknowledge requested libraries after
placing breakpoints.
---
 gdb/NEWS                      |   7 +++
 gdb/doc/gdb.texinfo           |  75 +++++++++++++++++++++++++----
 gdb/features/library-list.dtd |  10 ++--
 gdb/remote.c                  |  81 ++++++++++++++++++++++++++++++++
 gdb/solib-target.c            |  59 ++++++++++++++++++++++-
 gdb/solib.c                   |  24 +++++++++-
 gdb/solist.h                  |   7 +++
 gdb/target-delegates-gen.c    |  50 ++++++++++++++++++++
 gdb/target.c                  |  16 +++++++
 gdb/target.h                  |  21 +++++++++
 gdbserver/dll.cc              | 100 ++++++++++++++++++++++++++++++++++++---
 gdbserver/dll.h               |  25 +++++++---
 gdbserver/server.cc           | 107 +++++++++++++++++++++++++++++++++++++++---
 gdbserver/server.h            |   4 ++
 gdbserver/target.cc           |  14 ++++++
 gdbserver/target.h            |  20 ++++++++
 16 files changed, 585 insertions(+), 35 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index b60f66403116e73b4c5d2b2b4d847578da6e701a..698ea9d589870388c56782a3ec535899b768fe72 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -180,6 +180,13 @@ qXfer:libraries:read's response
   indicates support by supplying qXfer:libraries:read:in-memory-library+ in the
   qSupported packet.
 
+vAck:library
+vAck:in-memory-library
+
+  Acknowledge libraries to gdbserver when requested.  Libraries are acknowledged
+  after the initial processing by GDB such as loading symbols and placing
+  breakpoints.
+
 * Changed remote packets
 
 qXfer:features:read:target.xml
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index c84a8372c223e724e3042322a8bee07b12423050..03fcbee94198692b0e0bb1651add94d815e287f0 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -43447,6 +43447,43 @@ for success (@pxref{Stop Reply Packets})
 @cindex @samp{vStopped} packet
 @xref{Notification Packets}.
 
+@item vAck:@var{type}:@var{arg}[,@var{arg}@dots{}][;@var{type}:@var{arg}[,@var{arg}@dots{}]]@dots{}
+@cindex @samp{vAck} packet
+@anchor{vAck packet}
+
+Acknowledge a @samp{;}-separated list of remote stub responses, each
+with a @samp{,}-separated list of arguments defined by its @var{type}.
+The following @var{type}s with their respective arguments are
+supported:
+
+@table @samp
+@item library:@var{name}
+Acknowledge the shared library that had been reported as
+@samp{<library name="@var{name}" ack="yes">} in the remote stub's
+@samp{qXfer:libraries:read} response.  @value{GDBN} acknowledges
+libraries after initial processing like loading symbols and placing
+breakpoints.
+
+@item in-memory-library:@var{begin},@var{end}
+Acknowledge the shared library that had been reported as
+@samp{<in-memory-library begin="@var{begin}" end="@var{end}" ack="yes">}
+in the remote stub's @samp{qXfer:libraries:read} response.
+@value{GDBN} acknowledges libraries after initial processing like
+loading symbols and placing breakpoints.
+@end table
+
+Reply:
+@table @samp
+@item OK
+for success
+@item E @var{nn}
+for an error
+@end table
+
+@value{GDBN} indicates support for acknowledging individual types of
+responses by supplying an appropriate @samp{qSupported} feature
+(@pxref{qSupported}) for each type that it supports.
+
 @item X @var{addr},@var{length}:@var{XX@dots{}}
 @anchor{X packet}
 @cindex @samp{X} packet
@@ -44785,6 +44822,14 @@ didn't support @samp{E.@var{errtext}}, and older versions of
 
 New packets should be written to support @samp{E.@var{errtext}}
 regardless of this feature being true or not.
+
+@item vAck:library
+This feature indicates whether @value{GDBN} supports acknowledging
+libraries reported by name.
+
+@item vAck:in-memory-library
+This feature indicates whether @value{GDBN} supports acknowledging
+in-memory libraries reported by begin and end target address.
 @end table
 
 Stubs should ignore any unknown values for
@@ -48031,6 +48076,13 @@ 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.
 
+It may optionally contain a request for acknowledging that library.
+@value{GDBN} indicates support for acknowledging libraries by
+supplying an appropriate @samp{qSupported} feature
+(@pxref{qSupported}).  The remote stub must not request
+acknowledgement of libraries unless @value{GDBN} indicated support for
+it.
+
 For the common case of libraries that are fully linked binaries, the
 library should have a list of segments.  If the target supports
 dynamic linking of a relocatable object file, its library XML element
@@ -48056,16 +48108,21 @@ offset, looks like this:
 </library-list>
 @end smallexample
 
-A corresponding memory map for an in-memory library looks like this:
+A corresponding memory map for an in-memory library with a request for
+acknowledgement looks like this:
 
 @smallexample
 <library-list>
-  <in-memory-library begin="0xa000000" end="0xa001000">
+  <in-memory-library begin="0xa000000" end="0xa001000" ack="yes">
     <segment address="0x10000000"/>
   </library>
 </library-list>
 @end smallexample
 
+@value{GDBN} will acknowledge the library with a @samp{vAck;library}
+or, as in this case, a @samp{vAck;in-memory-library} packet.
+@xref{vAck packet}.
+
 Another simple memory map, with one loaded library with three
 allocated sections (.text, .data, .bss), looks like this:
 
@@ -48084,16 +48141,18 @@ The format of a library list is described by this DTD:
 @smallexample
 <!-- library-list: Root element with versioning -->
 <!ELEMENT library-list            (library | in-memory-library)*>
-<!ATTLIST library-list            version CDATA   #FIXED  "1.1">
+<!ATTLIST library-list            version CDATA        #FIXED  "1.2">
 <!ELEMENT library                 (segment*, section*)>
-<!ATTLIST library                 name    CDATA   #REQUIRED>
+<!ATTLIST library                 name    CDATA        #REQUIRED
+                                  ack     (yes | no)   'no'>
 <!ELEMENT in-memory-library       (segment*, section*)>
-<!ATTLIST in-memory-library       begin    CDATA   #REQUIRED
-                                  end      CDATA   #REQUIRED>
+<!ATTLIST in-memory-library       begin    CDATA       #REQUIRED
+                                  end      CDATA       #REQUIRED
+                                  ack      (yes | no)  'no'>
 <!ELEMENT segment                 EMPTY>
-<!ATTLIST segment                 address CDATA   #REQUIRED>
+<!ATTLIST segment                 address CDATA        #REQUIRED>
 <!ELEMENT section                 EMPTY>
-<!ATTLIST section                 address CDATA   #REQUIRED>
+<!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 f55071c8e906f091752e8ca78ec29bcd76028433..473b8eaa719b1d310f9eb4fe8f531df85008845d 100644
--- a/gdb/features/library-list.dtd
+++ b/gdb/features/library-list.dtd
@@ -6,14 +6,16 @@
 
 <!-- library-list: Root element with versioning -->
 <!ELEMENT library-list  (library | in-memory-library)*>
-<!ATTLIST library-list  version CDATA   #FIXED  "1.1">
+<!ATTLIST library-list  version CDATA   #FIXED  "1.2">
 
 <!ELEMENT library       (segment*, section*)>
-<!ATTLIST library       name    CDATA   #REQUIRED>
+<!ATTLIST library       name    CDATA        #REQUIRED
+                        ack     (yes | no)   'no'>
 
 <!ELEMENT in-memory-library       (segment*, section*)>
-<!ATTLIST in-memory-library       begin    CDATA   #REQUIRED
-                                  end      CDATA   #REQUIRED>
+<!ATTLIST in-memory-library       begin    CDATA       #REQUIRED
+                                  end      CDATA       #REQUIRED
+                                  ack      (yes | no)  'no'>
 
 <!ELEMENT segment       EMPTY>
 <!ATTLIST segment       address CDATA   #REQUIRED>
diff --git a/gdb/remote.c b/gdb/remote.c
index 7d074a5df322d68ded8f96c4832bc8c247435a4f..281e616eb83008e75da8a18efaee7001ddbf6d0d 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -376,6 +376,12 @@ enum {
   /* Support remote CTRL-C.  */
   PACKET_vCtrlC,
 
+  /* Support acknowledging libraries.  */
+  PACKET_vAck_library,
+
+  /* Support acknowledging in-memory-libraries.  */
+  PACKET_vAck_in_memory_library,
+
   /* Support TARGET_WAITKIND_NO_RESUMED.  */
   PACKET_no_resumed,
 
@@ -1154,6 +1160,9 @@ class remote_target : public process_stratum_target
 
   bool is_address_tagged (gdbarch *gdbarch, CORE_ADDR address) override;
 
+  void ack_library (const char *name) override;
+  void ack_in_memory_library (CORE_ADDR begin, CORE_ADDR end) override;
+
 public: /* Remote specific methods.  */
 
   void remote_download_command_source (int num, ULONGEST addr,
@@ -5845,6 +5854,10 @@ static const struct protocol_feature remote_protocol_features[] = {
     PACKET_memory_tagging_feature },
   { "error-message", PACKET_ENABLE, remote_supported_packet,
     PACKET_accept_error_message },
+  { "vAck:library", PACKET_DISABLE, remote_supported_packet,
+    PACKET_vAck_library },
+  { "vAck:in-memory-library", PACKET_DISABLE, remote_supported_packet,
+    PACKET_vAck_in_memory_library },
 };
 
 static char *remote_support_xml;
@@ -5959,6 +5972,14 @@ remote_target::remote_query_supported ()
       remote_query_supported_append
 	(&q, "qXfer:libraries:read:in-memory-library+");
 
+      if (m_features.packet_set_cmd_state (PACKET_vAck_library)
+	  != AUTO_BOOLEAN_FALSE)
+	remote_query_supported_append (&q, "vAck:library+");
+
+      if (m_features.packet_set_cmd_state (PACKET_vAck_in_memory_library)
+	  != AUTO_BOOLEAN_FALSE)
+	remote_query_supported_append (&q, "vAck:in-memory-library+");
+
       /* Keep this one last to work around a gdbserver <= 7.10 bug in
 	 the qSupported:xmlRegisters=i386 handling.  */
       if (remote_support_xml != NULL
@@ -15679,6 +15700,60 @@ remote_target::vcont_r_supported ()
 	  && get_remote_state ()->supports_vCont.r);
 }
 
+void
+remote_target::ack_library (const char *name)
+{
+  struct remote_state *rs = get_remote_state ();
+  char *p = rs->buf.data ();
+  char *endp = p + get_remote_packet_size ();
+
+  xsnprintf (p, endp - p, "vAck:library:%s", name);
+
+  putpkt (rs->buf);
+  getpkt (&rs->buf, 0);
+
+  packet_result result
+    = m_features.packet_ok (rs->buf, PACKET_vAck_library);
+  switch (result.status ())
+    {
+    case PACKET_OK:
+      break;
+    case PACKET_UNKNOWN:
+      error (_("No support for acknowledging libraries."));
+    case PACKET_ERROR:
+      error (_("Acknowledging library '%s' failed: '%s'"), name,
+	     rs->buf.data ());
+    }
+}
+
+void
+remote_target::ack_in_memory_library (CORE_ADDR begin, CORE_ADDR end)
+{
+  struct remote_state *rs = get_remote_state ();
+  char *p = rs->buf.data ();
+  char *endp = p + get_remote_packet_size ();
+
+  xsnprintf (p, endp - p, "vAck:in-memory-library:%s,%s",
+	     core_addr_to_string_nz (begin), core_addr_to_string_nz (end));
+
+  putpkt (rs->buf);
+  getpkt (&rs->buf, 0);
+
+  packet_result result
+    = m_features.packet_ok (rs->buf, PACKET_vAck_in_memory_library);
+  switch (result.status ())
+    {
+    case PACKET_OK:
+      break;
+    case PACKET_UNKNOWN:
+      error (_("No support for acknowledging in-memory libraries."));
+    case PACKET_ERROR:
+      error (_("Failed to acknowledge in-memory library %s-%s: %s"),
+	     core_addr_to_string_nz (begin), core_addr_to_string_nz (end),
+	     rs->buf.data ());
+    }
+}
+
 /* The "set/show range-stepping" set hook.  */
 
 static void
@@ -16442,6 +16517,12 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
 
   add_packet_config_cmd (PACKET_vCtrlC, "vCtrlC", "ctrl-c", 0);
 
+  add_packet_config_cmd (PACKET_vAck_library,
+			 "vAck:library", "ack-library", 0);
+
+  add_packet_config_cmd (PACKET_vAck_in_memory_library,
+			 "vAck:in-memory-library", "ack-in-memory-library", 0);
+
   add_packet_config_cmd (PACKET_QThreadEvents, "QThreadEvents", "thread-events",
 			 0);
 
diff --git a/gdb/solib-target.c b/gdb/solib-target.c
index 7165aed7e22f87c96a3716397e9380aff03a2a61..bf74d79ecdb2309b7645aaf04ce9fed1bff51cff 100644
--- a/gdb/solib-target.c
+++ b/gdb/solib-target.c
@@ -53,6 +53,11 @@ struct lm_info_target final : public lm_info
      This is only valid if location == lm_in_memory.  */
   CORE_ADDR begin = 0ull, end = 0ull;
 
+  /* A flag saying whether library load and unload need to be acknowledged
+     to the target after processing the library and placing/removing
+     breakpoints.  */
+  bool need_ack = false;
+
   /* The target can either specify segment bases or section bases, not
      both.  */
 
@@ -132,6 +137,24 @@ library_list_start_section (struct gdb_xml_parser *parser,
   last->section_bases.push_back (address);
 }
 
+/* Handle the 'ack' attribute of <library> and <in-memory-library>.  */
+
+static void
+library_ack (lm_info_target &item, std::vector<gdb_xml_value> &attributes)
+{
+  gdb_xml_value *ack = xml_find_attribute (attributes, "ack");
+  if (ack != nullptr)
+    {
+      const char *value = (const char *) ack->value.get ();
+      if (strcmp (value, "yes") == 0)
+	item.need_ack = true;
+      else if (strcmp (value, "no") == 0)
+	item.need_ack = false;
+      else
+	warning (_("bad attribute value for library:ack"));
+    }
+}
+
 /* Handle the start of a <library> element.  */
 
 static void
@@ -146,6 +169,8 @@ library_list_start_library (struct gdb_xml_parser *parser,
   item->name
     = (const char *) xml_find_attribute (attributes, "name")->value.get ();
 
+  library_ack (*item, attributes);
+
   list->emplace_back (item);
 }
 
@@ -165,6 +190,8 @@ in_memory_library_list_start_library (struct gdb_xml_parser *parser,
   item->end = (CORE_ADDR) *(ULONGEST *)
     xml_find_attribute (attributes, "end")->value.get ();
 
+  library_ack (*item, attributes);
+
   list->emplace_back (item);
 }
 
@@ -196,7 +223,8 @@ library_list_start_list (struct gdb_xml_parser *parser,
     {
       const char *string = (const char *) version->value.get ();
 
-      if ((strcmp (string, "1.0") != 0) && (strcmp (string, "1.1") != 0))
+      if ((strcmp (string, "1.0") != 0) && (strcmp (string, "1.1") != 0)
+	  && (strcmp (string, "1.2") != 0))
 	gdb_xml_error (parser,
 		       _("Library list has unsupported version \"%s\""),
 		       string);
@@ -228,12 +256,14 @@ static const struct gdb_xml_element library_children[] = {
 
 static const struct gdb_xml_attribute library_attributes[] = {
   { "name", GDB_XML_AF_NONE, NULL, NULL },
+  { "ack", GDB_XML_AF_OPTIONAL, NULL, NULL },
   { 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 },
+  { "ack", GDB_XML_AF_OPTIONAL, NULL, NULL },
   { NULL, GDB_XML_AF_NONE, NULL, NULL }
 };
 
@@ -473,6 +503,32 @@ solib_target_in_dynsym_resolve_code (CORE_ADDR pc)
   return in_plt_section (pc);
 }
 
+static void
+solib_target_ack_library (solib &so)
+{
+  lm_info_target *lm
+    = gdb::checked_static_cast<lm_info_target *> (so.lm_info.get ());
+
+  if (!lm->need_ack)
+    return;
+
+  /* Try only once, whether we succeed or not.  */
+  lm->need_ack = false;
+  switch (lm->location)
+    {
+    case lm_on_disk:
+      target_ack_library (so.so_original_name.c_str ());
+      return;
+
+    case lm_in_memory:
+      target_ack_in_memory_library (lm->begin, lm->end);
+      return;
+    }
+
+  warning (_("bad solib location '%d' for %s."), lm->location,
+	   so.so_original_name.c_str ());
+}
+
 const solib_ops solib_target_so_ops =
 {
   solib_target_relocate_section_addresses,
@@ -489,4 +545,5 @@ const solib_ops solib_target_so_ops =
   nullptr,
   default_find_solib_addr,
   gdb_bfd_open_from_target_memory,
+  solib_target_ack_library,
 };
diff --git a/gdb/solib.c b/gdb/solib.c
index cb302f9215257ddff18c94f5be39e189b02d84e6..92fc5137a3d469f55043235a65dcadcc1f25fe96 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -967,6 +967,7 @@ solib_add (const char *pattern, int from_tty, int readsyms)
     if (from_tty)
       add_flags |= SYMFILE_VERBOSE;
 
+    std::list<solib *> added_solibs;
     for (solib &gdb : current_program_space->solibs ())
       if (!pattern || re_exec (gdb.so_name.c_str ()))
 	{
@@ -989,14 +990,23 @@ solib_add (const char *pattern, int from_tty, int readsyms)
 				styled_string (file_name_style.style (),
 					       gdb.so_name.c_str ()));
 		}
-	      else if (solib_read_symbols (gdb, add_flags))
-		loaded_any_symbols = true;
+	      else
+		added_solibs.emplace_back (&gdb);
 	    }
 	}
 
+    for (solib *gdb : added_solibs)
+      if (solib_read_symbols (*gdb, add_flags))
+	loaded_any_symbols = true;
+
     if (loaded_any_symbols)
       breakpoint_re_set ();
 
+    /* Acknowledge loading of new solibs.  This must be called after
+       breakpoints have been set in this newly loaded solib.  */
+    for (solib *gdb : added_solibs)
+      solib_ack_library (*gdb);
+
     if (from_tty && pattern && !any_matches)
       gdb_printf ("No loaded shared libraries match the pattern `%s'.\n",
 		  pattern);
@@ -1700,6 +1710,16 @@ default_find_solib_addr (solib &so)
   return {};
 }
 
+/* See solist.h.  */
+
+void solib_ack_library (solib &so)
+{
+  const solib_ops *ops = gdbarch_so_ops (current_inferior ()->arch ());
+
+  if (ops->ack_library != nullptr)
+    (*ops->ack_library) (so);
+}
+
 void _initialize_solib ();
 
 void
diff --git a/gdb/solist.h b/gdb/solist.h
index c4d053a5c6c0261f1176121b2bc1f904ac2424a4..f64e45d874f68c644d1c5b6b182c7667319b6fad 100644
--- a/gdb/solist.h
+++ b/gdb/solist.h
@@ -191,6 +191,10 @@ struct solib_ops
   gdb_bfd_ref_ptr (*bfd_open_from_target_memory) (CORE_ADDR addr,
 						  CORE_ADDR size,
 						  const char *target);
+
+  /* Acknowledge a library.  This is called from add_solib after loading
+     symbols and placing breakpoints.  */
+  void (*ack_library) (solib &so);
 };
 
 /* A unique pointer to a so_list.  */
@@ -215,4 +219,7 @@ extern gdb_bfd_ref_ptr solib_bfd_open (const char *in_pathname);
    unable to find an address within the library SO.  */
 extern std::optional<CORE_ADDR> default_find_solib_addr (solib &so);
 
+/* Acknowledge a library.  */
+extern void solib_ack_library (solib &so);
+
 #endif
diff --git a/gdb/target-delegates-gen.c b/gdb/target-delegates-gen.c
index dd20e1404c32f45c536756e6e09cdadfab29d79b..d3f1a7cc313832fd376c2c93c505f334ab9f3e33 100644
--- a/gdb/target-delegates-gen.c
+++ b/gdb/target-delegates-gen.c
@@ -190,6 +190,8 @@ struct dummy_target : public target_ops
   void call_history_from (ULONGEST arg0, int arg1, record_print_flags arg2) override;
   void call_history_range (ULONGEST arg0, ULONGEST arg1, record_print_flags arg2) override;
   bool augmented_libraries_svr4_read () override;
+  void ack_library (const char *arg0) override;
+  void ack_in_memory_library (CORE_ADDR arg0, CORE_ADDR arg1) override;
   const struct frame_unwind *get_unwinder () override;
   const struct frame_unwind *get_tailcall_unwinder () override;
   void prepare_to_generate_core () override;
@@ -367,6 +369,8 @@ struct debug_target : public target_ops
   void call_history_from (ULONGEST arg0, int arg1, record_print_flags arg2) override;
   void call_history_range (ULONGEST arg0, ULONGEST arg1, record_print_flags arg2) override;
   bool augmented_libraries_svr4_read () override;
+  void ack_library (const char *arg0) override;
+  void ack_in_memory_library (CORE_ADDR arg0, CORE_ADDR arg1) override;
   const struct frame_unwind *get_unwinder () override;
   const struct frame_unwind *get_tailcall_unwinder () override;
   void prepare_to_generate_core () override;
@@ -4194,6 +4198,52 @@ debug_target::augmented_libraries_svr4_read ()
   return result;
 }
 
+void
+target_ops::ack_library (const char *arg0)
+{
+  this->beneath ()->ack_library (arg0);
+}
+
+void
+dummy_target::ack_library (const char *arg0)
+{
+  tcomplain ();
+}
+
+void
+debug_target::ack_library (const char *arg0)
+{
+  gdb_printf (gdb_stdlog, "-> %s->ack_library (...)\n", this->beneath ()->shortname ());
+  this->beneath ()->ack_library (arg0);
+  gdb_printf (gdb_stdlog, "<- %s->ack_library (", this->beneath ()->shortname ());
+  target_debug_print_const_char_p (arg0);
+  gdb_puts (")\n", gdb_stdlog);
+}
+
+void
+target_ops::ack_in_memory_library (CORE_ADDR arg0, CORE_ADDR arg1)
+{
+  this->beneath ()->ack_in_memory_library (arg0, arg1);
+}
+
+void
+dummy_target::ack_in_memory_library (CORE_ADDR arg0, CORE_ADDR arg1)
+{
+  tcomplain ();
+}
+
+void
+debug_target::ack_in_memory_library (CORE_ADDR arg0, CORE_ADDR arg1)
+{
+  gdb_printf (gdb_stdlog, "-> %s->ack_in_memory_library (...)\n", this->beneath ()->shortname ());
+  this->beneath ()->ack_in_memory_library (arg0, arg1);
+  gdb_printf (gdb_stdlog, "<- %s->ack_in_memory_library (", this->beneath ()->shortname ());
+  target_debug_print_CORE_ADDR (arg0);
+  gdb_puts (", ", gdb_stdlog);
+  target_debug_print_CORE_ADDR (arg1);
+  gdb_puts (")\n", gdb_stdlog);
+}
+
 const struct frame_unwind *
 target_ops::get_unwinder ()
 {
diff --git a/gdb/target.c b/gdb/target.c
index b6d1abe82dbfd93df601c599fc03d744d9094d00..fc7bda11eefea6a8d6c24380d98554e6ea385269 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -4226,6 +4226,22 @@ target_done_generating_core (void)
   current_inferior ()->top_target ()->done_generating_core ();
 }
 
+/* See target.h.  */
+
+void
+target_ack_library (const char *name)
+{
+  current_inferior ()->top_target ()->ack_library (name);
+}
+
+/* See target.h.  */
+
+void
+target_ack_in_memory_library (CORE_ADDR begin, CORE_ADDR end)
+{
+  current_inferior ()->top_target ()->ack_in_memory_library (begin, end);
+}
+
 \f
 
 static char targ_desc[] =
diff --git a/gdb/target.h b/gdb/target.h
index be84b27fd43206cf63bc6161321171dfb01b00aa..630944cde3a78a2fa8f2ed32282f995b66dc274a 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -1320,6 +1320,21 @@ struct target_ops
     virtual bool augmented_libraries_svr4_read ()
       TARGET_DEFAULT_RETURN (false);
 
+    /* Acknowledge a library reported by name.
+
+       Libraries are acknowledged after initial processing like loading
+       symbols and placing breakpoints on request from the target.  */
+    virtual void ack_library (const char *name)
+      TARGET_DEFAULT_NORETURN (tcomplain ());
+
+    /* Acknowledge an in-memory library reported by begin and end target
+       addresses.
+
+       Libraries are acknowledged after initial processing like loading
+       symbols and placing breakpoints on request from the target.  */
+    virtual void ack_in_memory_library (CORE_ADDR begin, CORE_ADDR end)
+      TARGET_DEFAULT_NORETURN (tcomplain ());
+
     /* Those unwinders are tried before any other arch unwinders.  If
        SELF doesn't have unwinders, it should delegate to the
        "beneath" target.  */
@@ -2629,4 +2644,10 @@ extern void target_prepare_to_generate_core (void);
 /* See to_done_generating_core.  */
 extern void target_done_generating_core (void);
 
+/* See target_ops::ack_library.  */
+extern void target_ack_library (const char *name);
+
+/* See target_ops::ack_in_memory_library.  */
+extern void target_ack_in_memory_library (CORE_ADDR begin, CORE_ADDR end);
+
 #endif /* !defined (TARGET_H) */
diff --git a/gdbserver/dll.cc b/gdbserver/dll.cc
index c2c63e9a69a54ddede77e40ea33b4e19cbdc71af..bbc8169920cf5b099e12b3eaec1c272d9ed2bf0a 100644
--- a/gdbserver/dll.cc
+++ b/gdbserver/dll.cc
@@ -25,18 +25,24 @@
 /* Record a newly loaded DLL at BASE_ADDR for the current process.  */
 
 void
-loaded_dll (const char *name, CORE_ADDR base_addr)
+loaded_dll (const char *name, CORE_ADDR base_addr, bool need_ack)
 {
-  loaded_dll (current_process (), name, base_addr);
+  loaded_dll (current_process (), name, base_addr, need_ack);
 }
 
 /* Record a newly loaded DLL at BASE_ADDR for PROC.  */
 
 void
-loaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
+loaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr,
+	    bool need_ack)
 {
+  if (need_ack && !get_client_state ().vack_library_supported)
+    throw_error (NOT_SUPPORTED_ERROR,
+		 _("library acknowledgement not supported."));
+
   gdb_assert (proc != nullptr);
-  proc->all_dlls.emplace_back (name != nullptr ? name : "", base_addr);
+  proc->all_dlls.emplace_back (name != nullptr ? name : "", base_addr,
+			       need_ack);
   proc->dlls_changed = true;
 }
 
@@ -44,10 +50,16 @@ loaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
 
 void
 loaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
-	    CORE_ADDR base_addr)
+	    CORE_ADDR base_addr, bool need_ack)
 {
+  /* It suffices to assert support for on-disk library acknowledgement since we
+     can fall back to that.  */
+  if (need_ack && !get_client_state ().vack_library_supported)
+    throw_error (NOT_SUPPORTED_ERROR,
+		 _("library acknowledgement not supported."));
+
   gdb_assert (proc != nullptr);
-  proc->all_dlls.emplace_back (begin, end, base_addr);
+  proc->all_dlls.emplace_back (begin, end, base_addr, need_ack);
   proc->dlls_changed = true;
 }
 
@@ -60,6 +72,78 @@ unloaded_dll (const char *name, CORE_ADDR base_addr)
   unloaded_dll (current_process (), name, base_addr);
 }
 
+static void
+ack_dll (process_info *process, dll_info &dll)
+{
+  gdb_assert (dll.need_ack);
+
+  switch (dll.location)
+    {
+    case dll_info::on_disk:
+      /* Check if this is a temporary file for an in-memory library.  */
+      if (dll.begin == UNSPECIFIED_CORE_ADDR)
+	{
+	  target_ack_library (process, dll.name.c_str ());
+	  dll.need_ack = false;
+	  return;
+	}
+
+      [[fallthrough]];
+    case dll_info::in_memory:
+      target_ack_in_memory_library (process, dll.begin, dll.end);
+      dll.need_ack = false;
+      return;
+    }
+
+  internal_error (_("bad library location: %d."), dll.location);
+}
+
+void
+ack_dll (process_info *proc, const char *name)
+{
+  std::list<dll_info> &dlls = proc->all_dlls;
+  std::list<dll_info>::iterator it
+    = std::find_if (dlls.begin (), dlls.end (),
+		    [name] (const dll_info &dll)
+	{
+	  return (dll.name == std::string (name));
+	});
+
+  if (it != dlls.end ())
+    ack_dll (proc, *it);
+}
+
+void
+ack_dll (const char *name)
+{
+  ack_dll (current_process (), name);
+}
+
+void
+ack_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end)
+{
+  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)
+	{
+	  /* For root devices with multiple sub-devices, modules with
+	     identical start/end addresses may be received for different
+	     sub-devices.  Therefore we check for the 'NEED_ACK' flag in
+	     the search, too.  */
+	  return ((dll.begin == begin) && (dll.end == end) && dll.need_ack);
+	});
+
+  if (it != dlls.end ())
+    ack_dll (proc, *it);
+}
+
+void
+ack_dll (CORE_ADDR begin, CORE_ADDR end)
+{
+  ack_dll (current_process (), begin, end);
+}
+
 /* Record that the DLL with NAME and BASE_ADDR has been unloaded
    from PROC.  */
 
@@ -99,6 +183,8 @@ unloaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
     {
       /* DLL has been found so remove the entry and free associated
 	 resources.  */
+      if (iter->need_ack)
+	ack_dll (proc, *iter);
       proc->all_dlls.erase (iter);
       proc->dlls_changed = true;
     }
@@ -144,6 +230,8 @@ unloaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
     {
       /* DLL has been found so remove the entry and free associated
 	 resources.  */
+      if (iter->need_ack)
+	ack_dll (proc, *iter);
       proc->all_dlls.erase (iter);
       proc->dlls_changed = 1;
     }
diff --git a/gdbserver/dll.h b/gdbserver/dll.h
index 9bce28e70cf3939b5b062e4b9c0f0d2d10a1d1d7..fa65456db6da49d2700cc468ccb95a63a0c2059a 100644
--- a/gdbserver/dll.h
+++ b/gdbserver/dll.h
@@ -30,12 +30,15 @@ struct dll_info
     in_memory
   };
 
-  dll_info (const std::string &name_, CORE_ADDR base_addr_)
-    : location (on_disk), name (name_), base_addr (base_addr_)
+  dll_info (const std::string &name_, CORE_ADDR base_addr_, bool need_ack_)
+    : location (on_disk), name (name_), begin (0), end (0),
+      base_addr (base_addr_), need_ack (need_ack_)
   {}
 
-  dll_info (CORE_ADDR begin_, CORE_ADDR end_, CORE_ADDR base_addr_)
-    : location (in_memory), begin (begin_), end (end_), base_addr (base_addr_)
+  dll_info (CORE_ADDR begin_, CORE_ADDR end_, CORE_ADDR base_addr_,
+	    bool need_ack_)
+    : location (in_memory), begin (begin_), end (end_), base_addr (base_addr_),
+      need_ack (need_ack_)
   {}
 
   location_t location;
@@ -43,17 +46,25 @@ struct dll_info
   CORE_ADDR begin;
   CORE_ADDR end;
   CORE_ADDR base_addr;
+  bool need_ack;
 };
 
-extern void loaded_dll (const char *name, CORE_ADDR base_addr);
+/* Throws NOT_SUPPORTED_ERROR if library acknowledgement is requested
+  (NEED_ACK = TRUE) and not supported.  */
+extern void loaded_dll (const char *name, CORE_ADDR base_addr,
+			bool need_ack = false);
 extern void loaded_dll (process_info *proc, const char *name,
-			CORE_ADDR base_addr);
+			CORE_ADDR base_addr, bool need_ack = false);
 extern void loaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
-			CORE_ADDR base_addr);
+			CORE_ADDR base_addr, bool need_ack = false);
 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);
+extern void ack_dll (const char *name);
+extern void ack_dll (process_info *proc, const char *name);
+extern void ack_dll (CORE_ADDR begin, CORE_ADDR end);
+extern void ack_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end);
 
 #endif /* GDBSERVER_DLL_H */
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 90902099a1b5a84fe86cb8391bf983a5943e78c5..68ee52a7615b51c24e3c609bd78b525416b71420 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -1919,25 +1919,33 @@ dll_to_tmpfile (dll_info &dll)
 static std::string
 print_qxfer_libraries_entry (dll_info &dll)
 {
+  const client_state &cs = get_client_state ();
+
   switch (dll.location)
     {
     case dll_info::in_memory:
-      if (get_client_state ().in_memory_library_supported)
+      if (cs.in_memory_library_supported
+	  && (!dll.need_ack || cs.vack_in_memory_library_supported))
 	return string_printf
-	  ("  <in-memory-library begin=\"0x%s\" end=\"0x%s\">"
+	  ("  <in-memory-library begin=\"0x%s\" end=\"0x%s\"%s>"
 	   "<segment address=\"0x%s\"/></in-memory-library>\n",
 	   paddress (dll.begin), paddress (dll.end),
-	   paddress (dll.base_addr));
+	   dll.need_ack ? " ack=\"yes\"" : "", paddress (dll.base_addr));
 
-      /* GDB does not support in-memory-library.  Fall back to storing it in a
-	 temporary file and report that file to GDB.  */
+      /* GDB does not support in-memory-library or acknowledging in-memory
+	 libraries.  Fall back to storing it in a temporary file and report that
+	 file to GDB.  */
       dll.name = dll_to_tmpfile (dll);
       [[fallthrough]];
 
     case dll_info::on_disk:
+      /* We checked this when requesting acknowledgement for DLL.  */
+      gdb_assert (!dll.need_ack || cs.vack_library_supported);
+
       return string_printf
-	("  <library name=\"%s\"><segment address=\"0x%s\"/></library>\n",
-	 dll.name.c_str (), paddress (dll.base_addr));
+	("  <library name=\"%s\"%s><segment address=\"0x%s\"/></library>\n",
+	 dll.name.c_str (), dll.need_ack ? " ack=\"yes\"" : "",
+	 paddress (dll.base_addr));
     }
 
   warning (_("unknown dll location: %x"), dll.location);
@@ -1970,6 +1978,18 @@ library_list_version_needed (const std::list<dll_info> &dlls)
 	    }
 	  break;
 	}
+
+      if (dll.need_ack)
+	{
+	  /* We checked support for acknowledgement when we inserted DLL.
+
+	     It suffices to assert support for on-disk library acknowledgement
+	     since we can fall back to that.  */
+	  gdb_assert (!dll.need_ack || cs.vack_library_supported);
+
+	  major = std::max (major, 1);
+	  minor = std::max (minor, 2);
+	}
     }
 
   return std::to_string (major) + std::string (".") + std::to_string (minor);
@@ -2854,6 +2874,10 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 		cs.error_message_supported = true;
 	      else if (feature == "qXfer:libraries:read:in-memory-library+")
 		cs.in_memory_library_supported = true;
+	      else if (feature == "vAck:library+")
+		cs.vack_library_supported = true;
+	      else if (feature == "vAck:in-memory-library+")
+		cs.vack_in_memory_library_supported = true;
 	      else
 		{
 		  /* Move the unknown features all together.  */
@@ -2984,6 +3008,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
       if (target_supports_memory_tagging ())
 	strcat (own_buf, ";memory-tagging+");
 
+      strcat (own_buf, ";vAck:library+");
+      strcat (own_buf, ";vAck:in-memory-library+");
+
       /* Reinitialize components as needed for the new connection.  */
       hostio_handle_new_gdb_connection ();
       target_handle_new_gdb_connection ();
@@ -3371,6 +3398,66 @@ err:
   return;
 }
 
+/* Parse vAck packets.  */
+
+static void
+handle_v_ack (char *own_buf)
+{
+  client_state &cs = get_client_state ();
+  char *p;
+
+  /* Move past vAck: to the first type string.  */
+  p = &own_buf[5];
+  do
+    {
+      if (cs.vack_library_supported
+	  && (strncmp (p, "library:", strlen ("library:")) == 0))
+	{
+	  p += strlen ("library:");
+
+	  /* We expect a single argument: the filename.  */
+	  const char *name = p;
+	  p = strchr (p, ';');
+	  if (p != nullptr)
+	    *p++ = '\0';
+
+	  ack_dll (name);
+	}
+      else if (cs.vack_in_memory_library_supported
+	       && (strncmp (p, "in-memory-library:",
+			    strlen ("in-memory-library:")) == 0))
+	{
+	  p += strlen ("in-memory-library:");
+
+	  /* We expect two arguments: begin and end address.  */
+	  CORE_ADDR begin, end;
+
+	  begin = (CORE_ADDR) strtoull (p, &p, 16);
+	  if (*p != ',')
+	    break;
+
+	  end = (CORE_ADDR) strtoull (p+1, &p, 16);
+	  if (*p == ';')
+	    p += 1;
+	  else if (*p != 0)
+	    break;
+
+	  ack_dll (begin, end);
+	}
+      else
+	break;
+    }
+  while (p != nullptr && *p != 0);
+
+  if (p == nullptr || *p == 0)
+    write_ok (own_buf);
+  else
+    {
+      std::string junk { p };
+      sprintf (own_buf, "E.junk in vAck: '%s'.", junk.c_str ());
+    }
+}
+
 /* Resume target with ACTIONS, an array of NUM_ACTIONS elements.  */
 
 static void
@@ -3717,6 +3804,12 @@ handle_v_requests (char *own_buf, int packet_len, int *new_packet_len)
       return;
     }
 
+  if (startswith (own_buf, "vAck:"))
+    {
+      handle_v_ack (own_buf);
+      return;
+    }
+
   if (handle_notif_ack (own_buf, packet_len))
     return;
 
diff --git a/gdbserver/server.h b/gdbserver/server.h
index 3bacd1062f8bf47720189d2d02b36ddb6089e6bc..ffbedc43cfc22bda65349751b6f23574ef7fd05b 100644
--- a/gdbserver/server.h
+++ b/gdbserver/server.h
@@ -199,6 +199,10 @@ struct client_state
      are not supported with qRcmd and m packets, but are still supported
      everywhere else.  This is for backward compatibility reasons.  */
   bool error_message_supported = false;
+
+  /* Track supported packets.  */
+  bool vack_library_supported = false;
+  bool vack_in_memory_library_supported = false;
 };
 
 client_state &get_client_state ();
diff --git a/gdbserver/target.cc b/gdbserver/target.cc
index 6db32da2e959284fea72f37ec2ddacf8071022e4..b07a4405c01ee7132bd26f83b603130f1d30e9fe 100644
--- a/gdbserver/target.cc
+++ b/gdbserver/target.cc
@@ -441,6 +441,20 @@ process_stratum_target::store_memtags (CORE_ADDR address, size_t len,
   gdb_assert_not_reached ("target op store_memtags not supported");
 }
 
+void
+process_stratum_target::ack_library (process_info *process, const char *name)
+{
+  gdb_assert_not_reached ("target op ack_library not supported");
+}
+
+void
+process_stratum_target::ack_in_memory_library (process_info *process,
+					       CORE_ADDR begin,
+					       CORE_ADDR end)
+{
+  gdb_assert_not_reached ("target op ack_in_memory_library not supported");
+}
+
 int
 process_stratum_target::read_offsets (CORE_ADDR *text, CORE_ADDR *data)
 {
diff --git a/gdbserver/target.h b/gdbserver/target.h
index 3643b9110dac760f3aadabe481b6f360d7210f5e..2fa88e09ffbc65c37fe468bbc5ea3580971ccfdb 100644
--- a/gdbserver/target.h
+++ b/gdbserver/target.h
@@ -516,6 +516,13 @@ 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);
+
+  /* Acknowledge a library reported by name.  */
+  virtual void ack_library (process_info *process, const char *name);
+
+  /* Acknowledge an in-memory library reported by address.  */
+  virtual void ack_in_memory_library (process_info *process, CORE_ADDR begin,
+				      CORE_ADDR end);
 };
 
 extern process_stratum_target *the_target;
@@ -713,6 +720,19 @@ target_thread_pending_child (thread_info *thread, target_waitkind *kind)
   return the_target->thread_pending_child (thread, kind);
 }
 
+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.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 12/47] gdb, solib, ze: solib_bfd_open_from_target_memory
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (10 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 11/47] gdb, gdbserver, rsp, ze: acknowledge libraries Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2025-07-18  0:42   ` Thiago Jung Bauermann
  2024-12-13 15:59 ` [PATCH v2 13/47] gdb, remote, ze: fix "$Hc-1#09...Packet received: E01" during startup Tankut Baris Aktemur
                   ` (35 subsequent siblings)
  47 siblings, 1 reply; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

From: Markus Metzger <markus.t.metzger@intel.com>

Add solib_bfd_open_from_target_memory to mimic the solib_bfd_open
behavior on top of gdb_bfd_open for in-memory files.
---
 gdb/solib-target.c |  2 +-
 gdb/solib.c        | 52 ++++++++++++++++++++++++++++++++++++++++------------
 gdb/solist.h       |  5 +++++
 3 files changed, 46 insertions(+), 13 deletions(-)

diff --git a/gdb/solib-target.c b/gdb/solib-target.c
index bf74d79ecdb2309b7645aaf04ce9fed1bff51cff..78c8b1c58b8c9b5bdff704c059aeea18fe9955dd 100644
--- a/gdb/solib-target.c
+++ b/gdb/solib-target.c
@@ -544,6 +544,6 @@ const solib_ops solib_target_so_ops =
   nullptr,
   nullptr,
   default_find_solib_addr,
-  gdb_bfd_open_from_target_memory,
+  solib_bfd_open_from_target_memory,
   solib_target_ack_library,
 };
diff --git a/gdb/solib.c b/gdb/solib.c
index 92fc5137a3d469f55043235a65dcadcc1f25fe96..a2d986dcd89841ee6ad7dbeff67b4f45042cc8d5 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -428,13 +428,34 @@ solib_bfd_fopen (const char *pathname, int fd)
   return abfd;
 }
 
+/* Initialize an opened BFD.  */
+
+static void
+solib_bfd_init (bfd *abfd)
+{
+
+  /* Check bfd format.  */
+  if (!bfd_check_format (abfd, bfd_object))
+    error (_("`%s': not in executable format: %s"),
+	   bfd_get_filename (abfd), bfd_errmsg (bfd_get_error ()));
+
+  /* Check bfd arch.  */
+  const struct bfd_arch_info *b
+    = gdbarch_bfd_arch_info (current_inferior ()->arch ());
+  if (!b->compatible (b, bfd_get_arch_info (abfd)))
+    error (_("`%s': Shared library architecture %s is not compatible "
+	     "with target architecture %s."),
+	   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
@@ -452,18 +473,25 @@ 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 (!bfd_check_format (abfd.get (), bfd_object))
-    error (_ ("`%s': not in executable format: %s"),
-	   bfd_get_filename (abfd.get ()), bfd_errmsg (bfd_get_error ()));
+  solib_bfd_init (abfd.get ());
 
-  /* Check bfd arch.  */
-  b = gdbarch_bfd_arch_info (current_inferior ()->arch ());
-  if (!b->compatible (b, bfd_get_arch_info (abfd.get ())))
-    error (_ ("`%s': Shared library architecture %s is not compatible "
-	      "with target architecture %s."),
-	   bfd_get_filename (abfd.get ()),
-	   bfd_get_arch_info (abfd.get ())->printable_name, b->printable_name);
+  return abfd;
+}
+
+/* See solist.h.  */
+
+gdb_bfd_ref_ptr
+solib_bfd_open_from_target_memory (CORE_ADDR begin, CORE_ADDR size,
+				   const char *target)
+{
+  /* Open bfd for shared library.  */
+  gdb_bfd_ref_ptr abfd
+    = gdb_bfd_open_from_target_memory (begin, 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 (begin), core_addr_to_string_nz (size));
+  solib_bfd_init (abfd.get ());
 
   return abfd;
 }
diff --git a/gdb/solist.h b/gdb/solist.h
index f64e45d874f68c644d1c5b6b182c7667319b6fad..4c30f4aaf9a23fd150c488ad390028ed410982e5 100644
--- a/gdb/solist.h
+++ b/gdb/solist.h
@@ -214,6 +214,11 @@ extern gdb_bfd_ref_ptr solib_bfd_fopen (const char *pathname, int fd);
 /* Find solib binary file and open it.  */
 extern gdb_bfd_ref_ptr solib_bfd_open (const char *in_pathname);
 
+/* Open an in-memory binary file.  */
+extern gdb_bfd_ref_ptr solib_bfd_open_from_target_memory (CORE_ADDR begin,
+							  CORE_ADDR size,
+							  const char *target);
+
 /* A default implementation of the solib_ops::find_solib_addr callback.
    This just returns an empty std::optional<CORE_ADDR> indicating GDB is
    unable to find an address within the library SO.  */

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 13/47] gdb, remote, ze: fix "$Hc-1#09...Packet received: E01" during startup
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (11 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 12/47] gdb, solib, ze: solib_bfd_open_from_target_memory Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2025-07-18  0:41   ` Thiago Jung Bauermann
  2024-12-13 15:59 ` [PATCH v2 14/47] gdb, infrun, ze: allow saving process events Tankut Baris Aktemur
                   ` (34 subsequent siblings)
  47 siblings, 1 reply; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

From: Markus Metzger <markus.t.metzger@intel.com>

When opening a connection, the remote target does

    set_continue_thread (minus_one_ptid);

which results in

    $Hc-1#09

to be send to gdbserver.  In remote-utils.c:read_ptid (), this is read as

    { <current inferior>, -1, 0 }

and not recognized as minus_one_ptid when handling 'H' in

	  ptid_t thread_id = read_ptid (&cs.own_buf[2], NULL);

	  if (thread_id == null_ptid || thread_id == minus_one_ptid)
	    thread_id = null_ptid;

Since minus_one_ptid and null_ptid are treated the same way, this is
probably what we want.

In remote_target::remote_resume_with_hc (), we do

      if (ptid == minus_one_ptid)
        set_continue_thread (any_thread_ptid);
      else
        set_continue_thread (ptid);

which amounts to the same thing as set_thread () does

      *buf++ = 'H';
      *buf++ = gen ? 'g' : 'c';
      if (ptid == magic_null_ptid)
        xsnprintf (buf, endbuf - buf, "0");
      else if (ptid == any_thread_ptid)
        xsnprintf (buf, endbuf - buf, "0");

so any_thread_ptid is pretty much the same as null_ptid and as
minus_one_ptid in this context.

Use any_thread_ptid to align with remote_target::remote_resume_with_hc ().
---
 gdb/remote.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gdb/remote.c b/gdb/remote.c
index 281e616eb83008e75da8a18efaee7001ddbf6d0d..3aaa1614211d617e0d7683eade88f0d00e1bee8b 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -5284,7 +5284,7 @@ remote_target::start_remote_1 (int from_tty, int extended_p)
       target_update_thread_list ();
 
       /* Let the stub know that we want it to return the thread.  */
-      set_continue_thread (minus_one_ptid);
+      set_continue_thread (any_thread_ptid);
 
       if (thread_count (this) == 0)
 	{

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 14/47] gdb, infrun, ze: allow saving process events
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (12 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 13/47] gdb, remote, ze: fix "$Hc-1#09...Packet received: E01" during startup Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 15/47] gdb, ze: add TARGET_WAITKIND_UNAVAILABLE Tankut Baris Aktemur
                   ` (33 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

From: Markus Metzger <markus.t.metzger@intel.com>

When a process stop reply is encountered during stop_all_threads (), a new
thread with the process' ptid is created and the waitstatus is saved in
that thread.  The effect is that we have a thread with a process ptid and
we dropped the event.

Save the waitstatus in the inferior, prevent that inferior from resuming,
and return it on the next wait.
---
 gdb/inferior.h |  4 ++++
 gdb/infrun.c   | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 55 insertions(+)

diff --git a/gdb/inferior.h b/gdb/inferior.h
index c08261bdcd3e3cc9a8d68ef4feb35a7655b50486..a3b46e8cc1824d1117fd8cb89665fae37d603240 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -64,6 +64,7 @@ struct thread_info;
 #include "displaced-stepping.h"
 
 #include <unordered_map>
+#include <optional>
 
 struct infcall_suspend_state;
 struct infcall_control_state;
@@ -319,6 +320,9 @@ struct inferior_control_state
 
   /* See the definition of stop_kind above.  */
   enum stop_kind stop_soon;
+
+  /* The waitstatus for this inferior's last event.  */
+  std::optional<target_waitstatus> waitstatus;
 };
 
 /* Initialize the inferior-related global state.  */
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 43eca814e296e9cd300bdc73a3388af1f2dc0113..5d778920afa7aa7553c62e060fce0ee87342c94b 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -2657,6 +2657,27 @@ resume_1 (enum gdb_signal sig)
   gdb_assert (!tp->stop_requested);
   gdb_assert (!thread_is_in_step_over_chain (tp));
 
+  inferior *inf = tp->inf;
+  process_stratum_target *target = inf->process_target ();
+  if (inf->control.waitstatus.has_value ())
+    {
+      infrun_debug_printf
+	("inferior %s has pending wait status %s.",
+	 target_pid_to_str (ptid_t (inf->pid)).c_str (),
+	 inf->control.waitstatus->to_string ().c_str ());
+
+      target->threads_executing = true;
+
+      if (target_can_async_p ())
+	{
+	  target_async (1);
+	  /* Tell the event loop we have an event to process.  */
+	  mark_async_event_handler (infrun_async_inferior_event_token);
+	}
+
+      return;
+    }
+
   if (tp->has_pending_waitstatus ())
     {
       infrun_debug_printf
@@ -4031,6 +4052,20 @@ 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);
 
+  /* Check if we have any saved waitstatus for the inferior itself.  */
+  if (inf->control.waitstatus.has_value ())
+    {
+      /* Wake up the event loop again, until all pending events are
+	 processed.  */
+      if (target_is_async_p ())
+	mark_async_event_handler (infrun_async_inferior_event_token);
+
+      *status = *inf->control.waitstatus;
+      inf->control.waitstatus.reset ();
+
+      return ptid_t (inf->pid);
+    }
+
   /* First check if there is a resumed thread with a wait status
      pending.  */
   if (ptid == minus_one_ptid || ptid.is_pid ())
@@ -5487,6 +5522,22 @@ handle_one (const wait_one_event &event)
 	    }
 	}
     }
+  else if (event.ptid.is_pid ())
+    {
+      /* This may be the first time we see the inferior report
+	 a stop.  */
+      inferior *inf = find_inferior_ptid (event.target, event.ptid);
+      if (inf->needs_setup)
+	setup_inferior (0);
+
+      /* This is needed to mark all the relevant threads in
+	 case the event is received from an all-stop
+	 target.  */
+      mark_non_executing_threads (event.target, event.ptid, event.ws);
+
+      /* Save the waitstatus for later.  */
+      *inf->control.waitstatus = event.ws;
+    }
   else
     {
       thread_info *t = event.target->find_thread (event.ptid);

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 15/47] gdb, ze: add TARGET_WAITKIND_UNAVAILABLE
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (13 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 14/47] gdb, infrun, ze: allow saving process events Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 16/47] gdb, infrun, ze: handle stopping unavailable threads Tankut Baris Aktemur
                   ` (32 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

From: Markus Metzger <markus.t.metzger@intel.com>

This new WAITKIND means that we cannot interact with the thread at the
moment.  The thread may become available again at a later time.

This will be used to model idle threads on Intel GT devices.
---
 gdb/fork-child.c        | 10 +++++++---
 gdb/gdbthread.h         | 12 +++++++++---
 gdb/infrun.c            | 22 +++++++++++++++++++++-
 gdb/nat/fork-inferior.c | 10 ++++++++++
 gdb/remote.c            |  6 +++++-
 gdb/target/waitstatus.c |  1 +
 gdb/target/waitstatus.h | 22 ++++++++++++++++++++++
 gdb/thread.c            |  2 +-
 8 files changed, 76 insertions(+), 9 deletions(-)

diff --git a/gdb/fork-child.c b/gdb/fork-child.c
index 539b11695d965a9e9523b04810fa5943c23b84d3..80372d023dac954eee32b40c468e3f0ce2c5dd87 100644
--- a/gdb/fork-child.c
+++ b/gdb/fork-child.c
@@ -124,14 +124,18 @@ gdb_startup_inferior (pid_t pid, int num_traps)
 {
   inferior *inf = current_inferior ();
   process_stratum_target *proc_target = inf->process_target ();
+  struct target_waitstatus ws;
 
   scoped_restore save_starting_up
     = make_scoped_restore (&inf->starting_up, true);
 
-  ptid_t ptid = startup_inferior (proc_target, pid, num_traps, NULL, NULL);
+  ptid_t ptid = startup_inferior (proc_target, pid, num_traps, &ws, NULL);
 
-  /* Mark all threads non-executing.  */
-  set_executing (proc_target, ptid, false);
+  if (ws.kind () != TARGET_WAITKIND_UNAVAILABLE)
+    {
+      /* Mark all threads non-executing.  */
+      set_executing (proc_target, ptid, false);
+    }
 
   return ptid;
 }
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 1011cf8f240f0c60c1f67af5b96189149c64a8f8..2dff0d4735fd3d2f1cd4da7c58e22bfd3c99a140 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -847,9 +847,15 @@ extern bool threads_are_executing (process_stratum_target *targ);
 /* Merge the executing property of thread PTID of TARG over to its
    thread state property (frontend running/stopped view).
 
-   "not executing" -> "stopped"
-   "executing"     -> "running"
-   "exited"        -> "exited"
+   "not executing or not resumed"	-> "stopped"
+   "executing and resumed"		-> "running"
+   "exited"				-> "exited"
+
+   On GPUs, threads may exist but not currently be available, e.g. because
+   they are idle or are executing a dispatch of another process.  We call
+   them unavailable and we model them as executing but not resumed.  From
+   the front-end perspective, they are stopped.  From the target
+   perspective, they are running.
 
    If PTID is minus_one_ptid, go over all threads of TARG.
 
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 5d778920afa7aa7553c62e060fce0ee87342c94b..d28a5867dcfd9fd7888c1d19008cb5606cb9116b 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -5434,7 +5434,19 @@ mark_non_executing_threads (process_stratum_target *target,
   else
     mark_ptid = event_ptid;
 
-  set_executing (target, mark_ptid, false);
+  /* Unavailable threads are still executing.
+
+     They were idle when we tried to stop them but they may start
+     executing work at any time.
+
+     In all-stop mode, because the target does not listen to debug
+     events, those threads are practically not executing.  But in
+     non-stop mode, the target can receive debug events from those
+     threads and the user can send interrupts to them.  So, we leave
+     them as executing.  */
+  if (!(target_is_non_stop_p ()
+	&& ws.kind () == TARGET_WAITKIND_UNAVAILABLE))
+    set_executing (target, mark_ptid, false);
 
   /* Likewise the resumed flag.  */
   set_resumed (target, mark_ptid, false);
@@ -6624,6 +6636,14 @@ handle_inferior_event (struct execution_control_state *ecs)
       interps_notify_no_history ();
       stop_waiting (ecs);
       return;
+
+    case TARGET_WAITKIND_UNAVAILABLE:
+      context_switch (ecs);
+      infrun_debug_printf ("unavailable");
+
+      stop_print_frame = false;
+      stop_waiting (ecs);
+      return;
     }
 }
 
diff --git a/gdb/nat/fork-inferior.c b/gdb/nat/fork-inferior.c
index 41765b102bc0a74ea5cd5586b2c25bdeb6e5d1a3..d70a3d616caddbdc508575be2e63ab8851beb8df 100644
--- a/gdb/nat/fork-inferior.c
+++ b/gdb/nat/fork-inferior.c
@@ -521,6 +521,16 @@ startup_inferior (process_stratum_target *proc_target, pid_t pid, int ntraps,
 	    resume_signal = ws.sig ();
 	    switch_to_thread (proc_target, event_ptid);
 	    break;
+
+	case TARGET_WAITKIND_UNAVAILABLE:
+	  /* We tried to interrupt the target but it responded that it is
+	     currently unavailable.
+
+	     There is no guarantee that it will become available any time
+	     soon.  That's good enough for starting up the inferior,
+	     however.  */
+	  switch_to_thread (proc_target, event_ptid);
+	  return resume_ptid;
 	}
 
       if (resume_signal != GDB_SIGNAL_TRAP)
diff --git a/gdb/remote.c b/gdb/remote.c
index 3aaa1614211d617e0d7683eade88f0d00e1bee8b..22c747de555e6a823973e17bbdb7c321364b547a 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -4974,7 +4974,11 @@ remote_target::process_initial_stop_replies (int from_tty)
 	  || ws.sig () != GDB_SIGNAL_0)
 	evthread->set_pending_waitstatus (ws);
 
-      set_executing (this, event_ptid, false);
+      /* Unavailable threads are executing (i.e. they may report events
+	 and we cannot access their state) but not running (i.e. we tried
+	 to stop them) from GDB's point of view.  */
+      if (ws.kind () != TARGET_WAITKIND_UNAVAILABLE)
+	set_executing (this, event_ptid, false);
       set_running (this, event_ptid, false);
       get_remote_thread_info (evthread)->set_not_resumed ();
     }
diff --git a/gdb/target/waitstatus.c b/gdb/target/waitstatus.c
index 9e9b5633b12d803dc2e9d9ac95d38a232525e86c..1cd0eee22367a8f907d7ded0300ee7df10841030 100644
--- a/gdb/target/waitstatus.c
+++ b/gdb/target/waitstatus.c
@@ -62,6 +62,7 @@ DIAGNOSTIC_ERROR_SWITCH
     case TARGET_WAITKIND_NO_HISTORY:
     case TARGET_WAITKIND_NO_RESUMED:
     case TARGET_WAITKIND_THREAD_CREATED:
+    case TARGET_WAITKIND_UNAVAILABLE:
       return str;
     }
 DIAGNOSTIC_POP
diff --git a/gdb/target/waitstatus.h b/gdb/target/waitstatus.h
index 7d5ad3f97769c62b2555ec3efcf2a5abe5fc91a2..3d1d1b918c75a04c804c4f3bd311927a64486692 100644
--- a/gdb/target/waitstatus.h
+++ b/gdb/target/waitstatus.h
@@ -107,6 +107,19 @@ enum target_waitkind
 
   /* The thread has exited.  The exit status is in value.integer.  */
   TARGET_WAITKIND_THREAD_EXITED,
+
+  /* The thread is unavailable.  We tried to stop it but it did not
+     respond in reasonable time.  Chances are that we won't be able to
+     stop it.
+
+     On GPUs, if we model hardware threads to avoid frequent entry/exit
+     notifications, idle threads may not respond to interrupts and hence
+     cannot be stopped by us.
+
+     They become responsive again when they pick up new work and they may
+     create events such as hitting breakpoints.  But we cannot tell when
+     this will happen - if at all.  */
+  TARGET_WAITKIND_UNAVAILABLE,
 };
 
 /* Determine if KIND represents an event with a new child - a fork,
@@ -165,6 +178,8 @@ DIAGNOSTIC_ERROR_SWITCH
       return "THREAD_CREATED";
     case TARGET_WAITKIND_THREAD_EXITED:
       return "THREAD_EXITED";
+    case TARGET_WAITKIND_UNAVAILABLE:
+      return "UNAVAILABLE";
   };
 DIAGNOSTIC_POP
 
@@ -368,6 +383,13 @@ struct target_waitstatus
     return *this;
   }
 
+  target_waitstatus &set_unavailable ()
+  {
+    this->reset ();
+    m_kind = TARGET_WAITKIND_UNAVAILABLE;
+    return *this;
+  }
+
   /* Get the kind of this wait status.  */
 
   target_waitkind kind () const
diff --git a/gdb/thread.c b/gdb/thread.c
index 5892b158603a7d11cb7c7efc3346f01714797201..fb2959078d860ac39e4d11e7db0e7226cfb24c3e 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -971,7 +971,7 @@ finish_thread_state (process_stratum_target *targ, ptid_t ptid)
   bool any_started = false;
 
   for (thread_info *tp : all_non_exited_threads (targ, ptid))
-    if (set_running_thread (tp, tp->executing ()))
+    if (set_running_thread (tp, tp->executing () && tp->resumed ()))
       any_started = true;
 
   if (any_started)

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 16/47] gdb, infrun, ze: handle stopping unavailable threads
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (14 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 15/47] gdb, ze: add TARGET_WAITKIND_UNAVAILABLE Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 17/47] gdb, infrun, ze: allow resuming " Tankut Baris Aktemur
                   ` (31 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

From: Markus Metzger <markus.t.metzger@intel.com>

On GPUs, threads may not always respond to interrupt requests.  In order
to still be able to support all-stop model on those targets, we allow this
case and model such threads as unavailable.

They do exist but we cannot interact with them.  In particular, they do
not have registers, including PC.

Use regcache_read_pc_protected () instead of regcache_read_pc () to
suppress an error when trying to access an unavailable thread's registers.
The thread's waitstatus will be TARGET_WAITKIND_UNAVAILABLE so there is
nothing extra to do here.
---
 gdb/infrun.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/gdb/infrun.c b/gdb/infrun.c
index d28a5867dcfd9fd7888c1d19008cb5606cb9116b..2abc2331e1821f3058ae00ee8908209dd7be020d 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -5610,7 +5610,7 @@ handle_one (const wait_one_event &event)
 	    }
 
 	  regcache = get_thread_regcache (t);
-	  t->set_stop_pc (regcache_read_pc (regcache));
+	  t->set_stop_pc (regcache_read_pc_protected (regcache));
 
 	  infrun_debug_printf ("saved stop_pc=%s for %s "
 			       "(currently_stepping=%d)",
@@ -6237,7 +6237,8 @@ handle_inferior_event (struct execution_control_state *ecs)
 
 	    handle_solib_event ();
 
-	    ecs->event_thread->set_stop_pc (regcache_read_pc (regcache));
+	    ecs->event_thread->set_stop_pc
+	      (regcache_read_pc_protected (regcache));
 	    address_space *aspace = ecs->event_thread->inf->aspace.get ();
 	    ecs->event_thread->control.stop_bpstat
 	      = bpstat_stop_status_nowatch (aspace,

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 17/47] gdb, infrun, ze: allow resuming unavailable threads
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (15 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 16/47] gdb, infrun, ze: handle stopping unavailable threads Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 18/47] gdb, gdbserver, ze: add U stop reply Tankut Baris Aktemur
                   ` (30 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

From: Markus Metzger <markus.t.metzger@intel.com>

On GPUs, threads may not always respond to interrupt requests when we try
to stabilize threads on an all-stop target.  In order to still be able to
support all-stop model on those targets, we allow this case and model such
threads as unavailable.

They do exist but we cannot interact with them.  In particular, they do
not have registers, including PC.

In some cases, all threads on a target may be unavailable.  This can
happen when we currently do not have any work scheduled on that device.
One particular instance of this is during startup when we newly attach to
the device.

We still need to allow 'continuing' those threads to make GDB listen to
events from that target.

Update gdb.threads/killed-outside.exp by adding another case to the
expected messages when killing the inferior.  It already contained cases
with and without thread exited messages.  Add another one for an exited
thread but no pc-not-available message.
---
 gdb/infrun.c                                 | 11 ++++++++++-
 gdb/testsuite/gdb.threads/killed-outside.exp |  4 ++++
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/gdb/infrun.c b/gdb/infrun.c
index 2abc2331e1821f3058ae00ee8908209dd7be020d..5d9dd1cbaf3f0cb5a56c52227ae818237958cd90 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -2733,7 +2733,16 @@ resume_1 (enum gdb_signal sig)
       step = false;
     }
 
-  CORE_ADDR pc = regcache_read_pc (regcache);
+  /* Allow resuming threads that do not have registers available.
+
+     This may happen on GPUs for threads that we tried to stop but that
+     did not respond.  We model them as unavailable threads.
+
+     PC will be zero in that case and we do not expect any breakpoints at
+     this address so we're not adding any extra checks.  */
+  CORE_ADDR pc = step
+    ? regcache_read_pc (regcache)
+    : regcache_read_pc_protected (regcache);
 
   infrun_debug_printf ("step=%d, signal=%s, trap_expected=%d, "
 		       "current thread [%s] at %s",
diff --git a/gdb/testsuite/gdb.threads/killed-outside.exp b/gdb/testsuite/gdb.threads/killed-outside.exp
index 35f7dc13896c1e653d0ffdb73301632e91783289..daa714beded981bea1bf7ddbc200eea799a44990 100644
--- a/gdb/testsuite/gdb.threads/killed-outside.exp
+++ b/gdb/testsuite/gdb.threads/killed-outside.exp
@@ -58,4 +58,8 @@ gdb_test_multiple "continue" "prompt after first continue" {
     -re -wrap ".*$killed_msg.*$no_longer_exists_msg" {
 	pass $gdb_test_name
     }
+    -re "Continuing\.\r\n\r\n$killed_msg\r\n$no_longer_exists_msg\r\n$gdb_prompt $" {
+	pass $gdb_test_name
+	gdb_test "continue" $not_being_run_msg "second continue"
+    }
 }

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 18/47] gdb, gdbserver, ze: add U stop reply
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (16 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 17/47] gdb, infrun, ze: allow resuming " Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 19/47] gdb, gdbserver, ze: add library notification to " Tankut Baris Aktemur
                   ` (29 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

From: Markus Metzger <markus.t.metzger@intel.com>

Add a new stop reply U for unavailable saying that we tried to stop a
process or thread but it would not respond and we cannot afford waiting.

This may occur when modeling threads as hardware threads on GPUs, where
threads that are currently idle cannot be interacted with.  We cannot
afford waiting for threads to become available again as that may require
submitting new work from the host process.  Or it may never happen for
some devices that simply are not used (anymore).

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
 gdb/NEWS                  |  6 ++++++
 gdb/doc/gdb.texinfo       | 24 ++++++++++++++++++++++++
 gdb/remote.c              | 18 +++++++++++++++++-
 gdbserver/remote-utils.cc |  5 +++++
 gdbserver/server.cc       | 28 ++++++++++++++++++++++++++++
 5 files changed, 80 insertions(+), 1 deletion(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index 698ea9d589870388c56782a3ec535899b768fe72..01b9a359538bcfa7f767f51f2dc59eb252ee8074 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -187,6 +187,12 @@ vAck:in-memory-library
   after the initial processing by GDB such as loading symbols and placing
   breakpoints.
 
+U stop reply
+
+  Indicates that threads are currently unavailable.  We tried stopping them but
+  they did not respond.  The remote stub reports support for this stop reply to
+  GDB's qSupported query.
+
 * Changed remote packets
 
 qXfer:features:read:target.xml
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 03fcbee94198692b0e0bb1651add94d815e287f0..1cf31d022843b41dfdc4014125438d5455dc0bb8 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -24683,6 +24683,10 @@ future connections is shown.  The available settings are:
 @tab @code{no resumed thread left stop reply}
 @tab Tracking thread lifetime.
 
+@item @code{unavailable-stop-reply}
+@tab @code{thread unavailable stop reply}
+@tab Tracking thread lifetime.
+
 @end multitable
 
 @cindex packet size, remote, configuring
@@ -43904,6 +43908,17 @@ reply packet from the target.  The latest @samp{C}, @samp{c}, @samp{S}
 or @samp{s} action is expected to be continued.  @xref{File-I/O Remote
 Protocol Extension}, for more details.
 
+@item U @var{thread-id}
+The program is currently unavailable.  The remote target tried to stop
+it but it would not respond.  The thread designator @var{thread-id}
+has the format and interpretation described in @ref{thread-id syntax}.
+
+This packet should not be sent by default; older @value{GDBN} versions
+did not support it.  @value{GDBN} requests it, by supplying an
+appropriate @samp{qSupported} feature (@pxref{qSupported}).  The
+remote stub must also supply the appropriate @samp{qSupported} feature
+indicating support.
+
 @end table
 
 @node General Query Packets
@@ -45128,6 +45143,11 @@ These are the currently defined stub features and their properties:
 @tab @samp{+}
 @tab No
 
+@item @samp{unavailable}
+@tab No
+@tab @samp{-}
+@tab No
+
 @end multitable
 
 These are the currently defined stub features, in more detail:
@@ -45374,6 +45394,10 @@ The remote stub supports replying with an error in a
 send this feature back to @value{GDBN} in the @samp{qSupported} reply,
 @value{GDBN} will always support @samp{E.@var{errtext}} format replies
 if it sent the @samp{error-message} feature.
+
+@item unavailable
+The remote stub reports the @samp{U} stop reply.
+
 @end table
 
 @item qSymbol::
diff --git a/gdb/remote.c b/gdb/remote.c
index 22c747de555e6a823973e17bbdb7c321364b547a..e277370ab4f1783d781c9b3a9edd4f459397839e 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -404,6 +404,9 @@ enum {
      errors, and so they should not need to check for this feature.  */
   PACKET_accept_error_message,
 
+  /* Support TARGET_WAITKIND_UNAVAILABLE.  */
+  PACKET_unavailable,
+
   PACKET_MAX
 };
 
@@ -5858,6 +5861,8 @@ static const struct protocol_feature remote_protocol_features[] = {
     PACKET_memory_tagging_feature },
   { "error-message", PACKET_ENABLE, remote_supported_packet,
     PACKET_accept_error_message },
+  { "unavailable", PACKET_DISABLE, remote_supported_packet,
+    PACKET_unavailable },
   { "vAck:library", PACKET_DISABLE, remote_supported_packet,
     PACKET_vAck_library },
   { "vAck:in-memory-library", PACKET_DISABLE, remote_supported_packet,
@@ -5973,6 +5978,10 @@ remote_target::remote_query_supported ()
 	  != AUTO_BOOLEAN_FALSE)
 	remote_query_supported_append (&q, "memory-tagging+");
 
+      if (m_features.packet_set_cmd_state (PACKET_unavailable)
+	  != AUTO_BOOLEAN_FALSE)
+	remote_query_supported_append (&q, "unavailable+");
+
       remote_query_supported_append
 	(&q, "qXfer:libraries:read:in-memory-library+");
 
@@ -8359,6 +8368,10 @@ Packet: '%s'\n"),
       event->ws.set_no_resumed ();
       event->ptid = minus_one_ptid;
       break;
+    case 'U':
+      event->ws.set_unavailable ();
+      event->ptid = read_ptid (&buf[1], NULL);
+      break;
     }
 }
 
@@ -8772,7 +8785,7 @@ remote_target::wait_as (ptid_t ptid, target_waitstatus *status,
 	     again.  Keep waiting for events.  */
 	  rs->waiting_for_stop_reply = 1;
 	  break;
-	case 'N': case 'T': case 'S': case 'X': case 'W': case 'w':
+	case 'N': case 'T': case 'S': case 'X': case 'W': case 'w': case 'U':
 	  {
 	    /* There is a stop reply to handle.  */
 	    rs->waiting_for_stop_reply = 0;
@@ -16545,6 +16558,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
   add_packet_config_cmd (PACKET_accept_error_message,
 			 "error-message", "error-message", 0);
 
+  add_packet_config_cmd (PACKET_unavailable,
+			 "U stop reply", "unavailable-stop-reply", 0);
+
   /* Assert that we've registered "set remote foo-packet" commands
      for all packet configs.  */
   {
diff --git a/gdbserver/remote-utils.cc b/gdbserver/remote-utils.cc
index 42252bad78f0dec91f887102d8a3baebb952c5ae..bbf72146ee8d301a9540211ef636dc0f6c223ef6 100644
--- a/gdbserver/remote-utils.cc
+++ b/gdbserver/remote-utils.cc
@@ -1284,6 +1284,11 @@ prepare_resume_reply (char *buf, ptid_t ptid, const target_waitstatus &status)
     case TARGET_WAITKIND_NO_RESUMED:
       sprintf (buf, "N");
       break;
+    case TARGET_WAITKIND_UNAVAILABLE:
+      sprintf (buf, "U");
+      buf += strlen (buf);
+      buf = write_ptid (buf, ptid);
+      break;
     default:
       error ("unhandled waitkind");
       break;
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 68ee52a7615b51c24e3c609bd78b525416b71420..eb541a9917f0faf6b4805700216e589054dc19bd 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -86,6 +86,9 @@ bool run_once;
 /* Whether to report TARGET_WAITKIND_NO_RESUMED events.  */
 static bool report_no_resumed;
 
+/* Whether to report TARGET_WAITKIND_UNAVAILABLE events.  */
+static bool report_unavailable;
+
 /* The event loop checks this to decide whether to continue accepting
    events.  */
 static bool keep_processing_events = true;
@@ -2872,6 +2875,12 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 		}
 	      else if (feature == "error-message+")
 		cs.error_message_supported = true;
+	      else if (feature == "unavailable+")
+		{
+		  /* GDB supports and wants TARGET_WAITKIND_UNAVAILABLE
+		     events.  */
+		  report_unavailable = true;
+		}
 	      else if (feature == "qXfer:libraries:read:in-memory-library+")
 		cs.in_memory_library_supported = true;
 	      else if (feature == "vAck:library+")
@@ -3501,6 +3510,16 @@ resume (struct thread_resume *actions, size_t num_actions)
 	  return;
 	}
 
+      if (cs.last_status.kind () == TARGET_WAITKIND_UNAVAILABLE
+	  && !report_unavailable)
+	{
+	  /* The client does not support this stop reply.  At least
+	     return error.  */
+	  sprintf (cs.own_buf, "E.Unavailable.");
+	  disable_async_io ();
+	  return;
+	}
+
       if (cs.last_status.kind () != TARGET_WAITKIND_EXITED
 	  && cs.last_status.kind () != TARGET_WAITKIND_SIGNALLED
 	  && cs.last_status.kind () != TARGET_WAITKIND_THREAD_EXITED
@@ -5188,6 +5207,15 @@ handle_target_event (int err, gdb_client_data client_data)
       if (gdb_connected () && report_no_resumed)
 	push_stop_notification (null_ptid, cs.last_status);
     }
+  else if (cs.last_status.kind () == TARGET_WAITKIND_UNAVAILABLE)
+    {
+      /* Update the thread state but otherwise silently ignore this.
+
+	 We do need to report thread unavailability on resume or stop
+	 requests, but not as async target events.  */
+      if (current_thread != nullptr)
+	current_thread->last_status = cs.last_status;
+    }
   else if (cs.last_status.kind () != TARGET_WAITKIND_IGNORE)
     {
       int pid = cs.last_ptid.pid ();

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 19/47] gdb, gdbserver, ze: add library notification to U stop reply
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (17 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 18/47] gdb, gdbserver, ze: add U stop reply Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 20/47] gdbserver, ze: report TARGET_WAITKIND_UNAVAILABLE events Tankut Baris Aktemur
                   ` (28 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

From: Markus Metzger <markus.t.metzger@intel.com>

If libraries have changed, add a ";library" notification to a 'U' stop
reply packet and handle it at the GDB side.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
 gdb/doc/gdb.texinfo       |  5 +++++
 gdb/remote.c              | 11 +++++++++--
 gdbserver/remote-utils.cc | 22 +++++++++++++++++++---
 3 files changed, 33 insertions(+), 5 deletions(-)

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 1cf31d022843b41dfdc4014125438d5455dc0bb8..365f74a5817ae82f729f5491dd3fde8ed24dbc2c 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -43909,10 +43909,15 @@ or @samp{s} action is expected to be continued.  @xref{File-I/O Remote
 Protocol Extension}, for more details.
 
 @item U @var{thread-id}
+@itemx U @var{thread-id};library
 The program is currently unavailable.  The remote target tried to stop
 it but it would not respond.  The thread designator @var{thread-id}
 has the format and interpretation described in @ref{thread-id syntax}.
 
+If @samp{;library} is appended, the loaded libraries have changed.
+@value{GDBN} should use @samp{qXfer:libraries:read} to fetch a new
+list of loaded libraries.
+
 This packet should not be sent by default; older @value{GDBN} versions
 did not support it.  @value{GDBN} requests it, by supplying an
 appropriate @samp{qSupported} feature (@pxref{qSupported}).  The
diff --git a/gdb/remote.c b/gdb/remote.c
index e277370ab4f1783d781c9b3a9edd4f459397839e..d55ca04fc6fce91fc01fac049959a55bd0af85f4 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -8369,8 +8369,15 @@ Packet: '%s'\n"),
       event->ptid = minus_one_ptid;
       break;
     case 'U':
-      event->ws.set_unavailable ();
-      event->ptid = read_ptid (&buf[1], NULL);
+      {
+	const char *opt = nullptr;
+
+	event->ws.set_unavailable ();
+	event->ptid = read_ptid (&buf[1], &opt);
+
+	if (strcmp (opt, ";library") == 0)
+	  event->ws.set_loaded ();
+      }
       break;
     }
 }
diff --git a/gdbserver/remote-utils.cc b/gdbserver/remote-utils.cc
index bbf72146ee8d301a9540211ef636dc0f6c223ef6..153701ceb1ecaa60702d17c62528997154887676 100644
--- a/gdbserver/remote-utils.cc
+++ b/gdbserver/remote-utils.cc
@@ -1285,9 +1285,25 @@ prepare_resume_reply (char *buf, ptid_t ptid, const target_waitstatus &status)
       sprintf (buf, "N");
       break;
     case TARGET_WAITKIND_UNAVAILABLE:
-      sprintf (buf, "U");
-      buf += strlen (buf);
-      buf = write_ptid (buf, ptid);
+      {
+	sprintf (buf, "U");
+	buf += strlen (buf);
+	buf = write_ptid (buf, ptid);
+
+	process_info *proc = find_process_pid (ptid.pid ());
+	gdb_assert (proc != nullptr);
+	if (proc->dlls_changed)
+	  {
+	    strcpy (buf, ";library");
+	    buf += strlen (buf);
+	    proc->dlls_changed = false;
+	  }
+
+	/* In non-stop, don't change the general thread behind
+	   GDB's back.  */
+	if (!non_stop)
+	  cs.general_thread = ptid;
+      }
       break;
     default:
       error ("unhandled waitkind");

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 20/47] gdbserver, ze: report TARGET_WAITKIND_UNAVAILABLE events
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (18 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 19/47] gdb, gdbserver, ze: add library notification to " Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 21/47] gdb, ze: handle TARGET_WAITKIND_UNAVAILABLE in stop_all_threads Tankut Baris Aktemur
                   ` (27 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

Do not ignore a TARGET_WAITKIND_UNAVAILABLE event.  This could be the result of

  1. GDB sending a stop request, but the thread being unavailable.
  2. target generating a library load event for which there is no
     actual stop event.

In case 1, if we do not inform GDB, it would wait indefinitely for the
thread it attempted to stop.

In case 2, if we do not inform GDB, the target may wait for an ack
which will never come from GDB.
---
 gdbserver/server.cc | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index eb541a9917f0faf6b4805700216e589054dc19bd..abaa6509041eb6b1c60ca4e99b2fdc1402d202da 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -5209,12 +5209,14 @@ handle_target_event (int err, gdb_client_data client_data)
     }
   else if (cs.last_status.kind () == TARGET_WAITKIND_UNAVAILABLE)
     {
-      /* Update the thread state but otherwise silently ignore this.
-
-	 We do need to report thread unavailability on resume or stop
-	 requests, but not as async target events.  */
+      /* Update the thread state and report the event.  GDB must have
+	 sent a stop request for the thread, which turned out to be
+	 unavailable.  To not make GDB wait indefinitely, we report
+	 the event.  */
       if (current_thread != nullptr)
 	current_thread->last_status = cs.last_status;
+
+      push_stop_notification (cs.last_ptid, cs.last_status);
     }
   else if (cs.last_status.kind () != TARGET_WAITKIND_IGNORE)
     {

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 21/47] gdb, ze: handle TARGET_WAITKIND_UNAVAILABLE in stop_all_threads
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (19 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 20/47] gdbserver, ze: report TARGET_WAITKIND_UNAVAILABLE events Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 22/47] gdb, remote: handle thread unavailability in print_one_stopped_thread Tankut Baris Aktemur
                   ` (26 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

If a TARGET_WAITKIND_UNAVAILABLE is received as the result of a stop
request, consider it the same as a stopped event with the 0 signal, and
do not save it as a pending event.  We would expect the target to send us
a TARGET_WAITKIND_STOPPED status if there were a stopped event.  We must
have received TARGET_WAITKIND_UNAVAILABLE because all the threads were
unavailable for our interrupt.  Hence, it must be OK to treat the
TARGET_WAITKIND_UNAVAILABLE status as an internal stop and not report
to the user.
---
 gdb/infrun.c | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/gdb/infrun.c b/gdb/infrun.c
index 5d9dd1cbaf3f0cb5a56c52227ae818237958cd90..8cee2c6e4c59c70f49c350da9346930f525b7da9 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -5578,8 +5578,18 @@ handle_one (const wait_one_event &event)
 	  setup_inferior (0);
 	}
 
-      if (event.ws.kind () == TARGET_WAITKIND_STOPPED
-	  && event.ws.sig () == GDB_SIGNAL_0)
+      /* If a TARGET_WAITKIND_UNAVAILABLE is received as the result of
+	 a stop request, consider it the same as a stopped event with
+	 the 0 signal, and do not save it as a pending event.  We
+	 would expect the target to send us a TARGET_WAITKIND_STOPPED
+	 status if there were a stopped event.  We must have received
+	 TARGET_WAITKIND_UNAVAILABLE because all the threads were
+	 unavailable for our interrupt.  Hence, it must be OK to treat
+	 the TARGET_WAITKIND_UNAVAILABLE status as an internal stop
+	 and report to the user.  */
+      if ((event.ws.kind () == TARGET_WAITKIND_STOPPED
+	   && event.ws.sig () == GDB_SIGNAL_0)
+	  || (event.ws.kind () == TARGET_WAITKIND_UNAVAILABLE))
 	{
 	  /* We caught the event that we intended to catch, so
 	     there's no event to save as pending.  */

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 22/47] gdb, remote: handle thread unavailability in print_one_stopped_thread
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (20 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 21/47] gdb, ze: handle TARGET_WAITKIND_UNAVAILABLE in stop_all_threads Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 23/47] gdb, remote: do 'remote_add_inferior' in 'remote_notice_new_inferior' earlier Tankut Baris Aktemur
                   ` (25 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

Take it into account in remote_target::print_one_stopped_thread that
the thread may be unavailable.
---
 gdb/remote.c | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/gdb/remote.c b/gdb/remote.c
index d55ca04fc6fce91fc01fac049959a55bd0af85f4..31e78fc0b93039df7c7fcff583f77495575ec8f6 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -4881,23 +4881,32 @@ remote_target::print_one_stopped_thread (thread_info *thread)
 {
   target_waitstatus ws;
 
+  bool is_unavailable
+    = (regcache_read_pc_protected (get_thread_regcache (thread)) == 0);
+
   /* If there is a pending waitstatus, use it.  If there isn't it's because
      the thread's stop was reported with TARGET_WAITKIND_STOPPED / GDB_SIGNAL_0
      and process_initial_stop_replies decided it wasn't interesting to save
-     and report to the core.  */
+     and report to the core.  Take it into account that the thread may be
+     unavailable.  */
   if (thread->has_pending_waitstatus ())
     {
       ws = thread->pending_waitstatus ();
       thread->clear_pending_waitstatus ();
     }
+  else if (is_unavailable)
+    ws.set_unavailable ();
   else
     {
       ws.set_stopped (GDB_SIGNAL_0);
     }
 
   switch_to_thread (thread);
-  thread->set_stop_pc (get_frame_pc (get_current_frame ()));
-  set_current_sal_from_frame (get_current_frame ());
+  if (!is_unavailable)
+    {
+      thread->set_stop_pc (get_frame_pc (get_current_frame ()));
+      set_current_sal_from_frame (get_current_frame ());
+    }
 
   /* For "info program".  */
   set_last_target_status (this, thread->ptid, ws);

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 23/47] gdb, remote: do 'remote_add_inferior' in 'remote_notice_new_inferior' earlier
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (21 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 22/47] gdb, remote: handle thread unavailability in print_one_stopped_thread Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 24/47] gdb, remote: handle a generic process PID in remote_notice_new_inferior Tankut Baris Aktemur
                   ` (24 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

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 | 23 +++++++++++------------
 1 file changed, 11 insertions(+), 12 deletions(-)

diff --git a/gdb/remote.c b/gdb/remote.c
index 31e78fc0b93039df7c7fcff583f77495575ec8f6..4dc79daf0a8618c5920dd9461d91273244326336 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -2991,6 +2991,17 @@ remote_target::remote_notice_new_inferior (ptid_t currthread, bool executing)
       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);
+	}
+
       if (inferior_ptid.is_pid ()
 	  && pid == inferior_ptid.pid ())
 	{
@@ -3021,18 +3032,6 @@ remote_target::remote_notice_new_inferior (ptid_t currthread, bool executing)
 	  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);
-	}
-
       /* This is really a new thread.  Add it.  */
       thread_info *new_thr
 	= remote_add_thread (currthread, running, executing, false);

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 24/47] gdb, remote: handle a generic process PID in remote_notice_new_inferior
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (22 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 23/47] gdb, remote: do 'remote_add_inferior' in 'remote_notice_new_inferior' earlier Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 25/47] gdb, remote: handle a generic process PID in process_stop_reply Tankut Baris Aktemur
                   ` (23 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

If the ptid received by 'remote_notice_new_inferior' is a generic pid,
return after checking for a new inferior.  Do not attempt checking for
and adding new threads.
---
 gdb/remote.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/gdb/remote.c b/gdb/remote.c
index 4dc79daf0a8618c5920dd9461d91273244326336..86f9a976a31841a0e53a7fbf0a7eff85f3368d5d 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -3002,6 +3002,11 @@ remote_target::remote_notice_new_inferior (ptid_t currthread, bool executing)
 	  inf = remote_add_inferior (fake_pid_p, pid, -1, 1);
 	}
 
+      /* We may have received the event for a process in general.
+	 There is nothing to check further in this case.  */
+      if (currthread.is_pid ())
+	return;
+
       if (inferior_ptid.is_pid ()
 	  && pid == inferior_ptid.pid ())
 	{

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 25/47] gdb, remote: handle a generic process PID in process_stop_reply
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (23 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 24/47] gdb, remote: handle a generic process PID in remote_notice_new_inferior Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 26/47] gdb: use the pid from inferior in setup_inferior Tankut Baris Aktemur
                   ` (22 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

When processing a new stop reply, the received PID could be a generic
process id (i.e.: is_pid() == true) instead of a specific thread id.
Handle this case.
---
 gdb/remote.c | 25 +++++++++++++++----------
 1 file changed, 15 insertions(+), 10 deletions(-)

diff --git a/gdb/remote.c b/gdb/remote.c
index 86f9a976a31841a0e53a7fbf0a7eff85f3368d5d..c74f6451b6c1f2a1c2dbcc51c70d8fecc3d82b0e 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -8636,18 +8636,23 @@ remote_target::process_stop_reply (stop_reply_up stop_reply,
 	    }
 	}
 
-      remote_thread_info *remote_thr = get_remote_thread_info (this, ptid);
-      remote_thr->core = stop_reply->core;
-      remote_thr->stop_reason = stop_reply->stop_reason;
-      remote_thr->watch_data_address = stop_reply->watch_data_address;
-
-      if (target_is_non_stop_p ())
+      if (!ptid.is_pid ())
 	{
-	  /* If the target works in non-stop mode, a stop-reply indicates that
-	     only this thread stopped.  */
-	  remote_thr->set_not_resumed ();
+	  remote_thread_info *remote_thr
+	    = get_remote_thread_info (this, ptid);
+	  remote_thr->core = stop_reply->core;
+	  remote_thr->stop_reason = stop_reply->stop_reason;
+	  remote_thr->watch_data_address = stop_reply->watch_data_address;
+
+	  if (target_is_non_stop_p ())
+	    {
+	      /* If the target works in non-stop mode, a stop-reply
+		 indicates that only this thread stopped.  */
+	      remote_thr->set_not_resumed ();
+	    }
 	}
-      else
+
+      if (!target_is_non_stop_p ())
 	{
 	  /* If the target works in all-stop mode, a stop-reply indicates that
 	     all the target's threads stopped.  */

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 26/47] gdb: use the pid from inferior in setup_inferior
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (24 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 25/47] gdb, remote: handle a generic process PID in process_stop_reply Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 27/47] gdb: revise the pid_to_exec_file target op Tankut Baris Aktemur
                   ` (21 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

This is a step to reduce the dependency on the global inferior_ptid
variable.
---
 gdb/infcmd.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 5c0e3f51162803d2aa4e4ca0b41475d616deda29..27473123c1a0b2702b81d0b28c41931b4e39dd52 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -2501,7 +2501,7 @@ setup_inferior (int from_tty)
   /* If no exec file is yet known, try to determine it from the
      process itself.  */
   if (current_program_space->exec_filename () == nullptr)
-    exec_file_locate_attach (inferior_ptid.pid (), 1, from_tty);
+    exec_file_locate_attach (inferior->pid, 1, from_tty);
   else
     {
       reopen_exec_file ();
@@ -2509,7 +2509,7 @@ setup_inferior (int from_tty)
     }
 
   /* Take any necessary post-attaching actions for this platform.  */
-  target_post_attach (inferior_ptid.pid ());
+  target_post_attach (inferior->pid);
 
   post_create_inferior (from_tty);
 }

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 27/47] gdb: revise the pid_to_exec_file target op
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (25 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 26/47] gdb: use the pid from inferior in setup_inferior Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 28/47] gdb: load solibs if the target does not have the notion of an exec file Tankut Baris Aktemur
                   ` (20 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

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 82d9266b7e3669dabfe29c5362498d60be9fa6c5..e495982ca2085d6e23a13b1662f0d4e1779b5e74 100644
--- a/gdb/exec.c
+++ b/gdb/exec.c
@@ -331,6 +331,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;
+    }
+
   gdb::unique_xmalloc_ptr<char> exec_file_host
     = exec_file_find (exec_file_target, NULL);
 
diff --git a/gdb/target.h b/gdb/target.h
index 630944cde3a78a2fa8f2ed32282f995b66dc274a..683d786b55861193dde7f78b8225d7b20f2c0944 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -2004,6 +2004,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,
+   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.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 28/47] gdb: load solibs if the target does not have the notion of an exec file
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (26 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 27/47] gdb: revise the pid_to_exec_file target op Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 29/47] gdbserver: import AC_LIB_HAVE_LINKFLAGS macro into the autoconf script Tankut Baris Aktemur
                   ` (19 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

In the 'post_create_inferior' function, the code block that calls the
solib ops' post-create-inferior hook function is guarded by a
null-check on exec_bfd.  This prevents loading solibs of an inferior
if it does not have an exec_bfd.  However, such a scenario could be
possible with relatively newer targets, such as GPUs, where an
executable does not exist but the GPU kernels are treated as
libraries.

Add a check for this case.
---
 gdb/infcmd.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 27473123c1a0b2702b81d0b28c41931b4e39dd52..78e7bbb0de048f5973d5803731a6f01cf30047c3 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -260,7 +260,15 @@ post_create_inferior (int from_tty)
 	throw;
     }
 
-  if (current_program_space->exec_bfd ())
+  /* Some targets (e.g.  GPUs) may still have solibs although they do
+     not have the notion of an exec file.  */
+  const char *exec_file
+    = target_pid_to_exec_file (current_inferior ()->pid);
+  bool target_may_have_solibs
+    = (exec_file != nullptr && *exec_file == '\0');
+
+  if (current_program_space->exec_bfd () != nullptr
+      || target_may_have_solibs)
     {
       const unsigned solib_add_generation
 	= current_program_space->solib_add_generation;

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 29/47] gdbserver: import AC_LIB_HAVE_LINKFLAGS macro into the autoconf script
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (27 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 28/47] gdb: load solibs if the target does not have the notion of an exec file Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 30/47] gdbserver: add a pointer to the owner thread in regcache Tankut Baris Aktemur
                   ` (18 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

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 5a00c6e9878bd5e3effa849685a49b15867deec0..44dae81e1387adc2fccd29ddd685d2e18f357f1a 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.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 30/47] gdbserver: add a pointer to the owner thread in regcache
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (28 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 29/47] gdbserver: import AC_LIB_HAVE_LINKFLAGS macro into the autoconf script Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 31/47] gdbserver: dump 'xx...x' in collect_register_as_string for unavailable register Tankut Baris Aktemur
                   ` (17 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

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 f1d63eac0ba4c2dccfb3c54910e6cdb768931f61..5e4ef579d0f058220e42460cf81dad18d3f6f868 100644
--- a/gdbserver/regcache.cc
+++ b/gdbserver/regcache.cc
@@ -44,6 +44,7 @@ get_thread_regcache (thread_info *thread, int fetch)
 
       regcache = new_register_cache (proc->tdesc);
       thread->set_regcache (regcache);
+      regcache->thread = thread;
     }
 
   if (fetch && regcache->registers_valid == 0)
diff --git a/gdbserver/regcache.h b/gdbserver/regcache.h
index 16ee4d0a2e7127127225b72d8e9ef3eab71ce88e..62c014d16d8d184044b803087a26da86b7627e90 100644
--- a/gdbserver/regcache.h
+++ b/gdbserver/regcache.h
@@ -33,6 +33,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 valid.  If false, we
      haven't fetched the registers from the target yet.  Not that this
      register cache is _not_ pass-through, unlike GDB's.  Note that

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 31/47] gdbserver: dump 'xx...x' in collect_register_as_string for unavailable register
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (29 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 30/47] gdbserver: add a pointer to the owner thread in regcache Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-23 11:38   ` Aktemur, Tankut Baris
  2024-12-13 15:59 ` [PATCH v2 32/47] gdbserver: wait for stopped threads in queue_stop_reply_callback Tankut Baris Aktemur
                   ` (16 subsequent siblings)
  47 siblings, 1 reply; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

Fix 'collect_register_as_string' so that unavailable registers are
dumped as 'xx...x' instead of arbitrary values.  This gives the
opportunity that we can reuse 'collect_register_as_string' in
'registers_to_string' for additional code simplification.
---
 gdbserver/regcache.cc | 25 +++++++++++--------------
 1 file changed, 11 insertions(+), 14 deletions(-)

diff --git a/gdbserver/regcache.cc b/gdbserver/regcache.cc
index 5e4ef579d0f058220e42460cf81dad18d3f6f868..24793c86173ba982e4601610e54ed0688a8f929b 100644
--- a/gdbserver/regcache.cc
+++ b/gdbserver/regcache.cc
@@ -210,24 +210,13 @@ find_register_by_number (const struct target_desc *tdesc, int n)
 void
 registers_to_string (struct regcache *regcache, char *buf)
 {
-  unsigned char *registers = regcache->registers;
   const struct target_desc *tdesc = regcache->tdesc;
 
   for (int i = 0; i < tdesc->reg_defs.size (); ++i)
     {
-      if (regcache->register_status[i] == REG_VALID)
-	{
-	  bin2hex (registers, buf, register_size (tdesc, i));
-	  buf += register_size (tdesc, i) * 2;
-	}
-      else
-	{
-	  memset (buf, 'x', register_size (tdesc, i) * 2);
-	  buf += register_size (tdesc, i) * 2;
-	}
-      registers += register_size (tdesc, i);
+      collect_register_as_string (regcache, i, buf);
+      buf += register_size (tdesc, i) * 2;
     }
-  *buf = '\0';
 }
 
 void
@@ -493,7 +482,15 @@ regcache_raw_get_unsigned_by_name (struct regcache *regcache,
 void
 collect_register_as_string (struct regcache *regcache, int n, char *buf)
 {
-  bin2hex (register_data (regcache, n), buf);
+  int reg_size = register_size (regcache->tdesc, n);
+
+  if (regcache->get_register_status (n) == REG_VALID)
+    bin2hex (register_data (regcache, n), buf);
+  else
+    memset (buf, 'x', reg_size * 2);
+
+  buf += reg_size * 2;
+  *buf = '\0';
 }
 
 void

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 32/47] gdbserver: wait for stopped threads in queue_stop_reply_callback
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (30 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 31/47] gdbserver: dump 'xx...x' in collect_register_as_string for unavailable register Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 33/47] gdbserver: adjust pid after the target attaches Tankut Baris Aktemur
                   ` (15 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

From: Markus Metzger <markus.t.metzger@intel.com>

In queue_stop_reply_callback, we check whether a thread is stopped in the
target and then assume that the thread's status is already filled in.

This would require targets to modify the thread's status rather than the
server setting it in response to a target wait call, which kind of breaks
the abstraction.

Call wait for a stopped thread and update the wait status before asserting
that it isn't TARGET_WAITKIND_IGNORE.
---
 gdbserver/server.cc | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index abaa6509041eb6b1c60ca4e99b2fdc1402d202da..b144d2b165564f1681e1082e230fdbbeac8d3bf6 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -3896,6 +3896,13 @@ queue_stop_reply_callback (thread_info *thread)
     {
       if (target_thread_stopped (thread))
 	{
+	  /* Wait for THREAD if we have not done that, already.  */
+	  client_state cs;
+	  cs.last_ptid = mywait (thread->id, &cs.last_status,
+				 TARGET_WNOHANG, 1);
+	  if (cs.last_ptid == thread->id)
+	    thread->last_status = cs.last_status;
+
 	  threads_debug_printf
 	    ("Reporting thread %s as already stopped with %s",
 	     target_pid_to_str (thread->id).c_str (),

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 33/47] gdbserver: adjust pid after the target attaches
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (31 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 32/47] gdbserver: wait for stopped threads in queue_stop_reply_callback Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 34/47] gdb: do not create a thread after a process event Tankut Baris Aktemur
                   ` (14 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

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 dfe4c6a39a0b3a6eaaf8aea71d45f2025bf09471..dd580b3f484d1700f1df1a229904fbcce5196544 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -1156,7 +1156,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;
@@ -1175,7 +1175,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 ());
     }
 
   open_proc_mem_file (proc);
@@ -1239,7 +1239,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 5be00b8c98cb87fb12277ac269d804e1efe602e1..05fb7985c4199fd5038f19998e333599a1ecfb65 100644
--- a/gdbserver/linux-low.h
+++ b/gdbserver/linux-low.h
@@ -145,7 +145,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 04e103563e793a06fbd90a7bbcb139da8a4b9a63..a246cc2befac7477f05fcce407a505250e79c46a 100644
--- a/gdbserver/netbsd-low.cc
+++ b/gdbserver/netbsd-low.cc
@@ -107,7 +107,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 53200ddffc42a8102f66f0808289589e65af8762..00a0287384a37c859dbb8216c6217020d4e1d583 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 b144d2b165564f1681e1082e230fdbbeac8d3bf6..db68ca6c74bd6e5b17565163906cccddabe80bcc 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -297,16 +297,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 2fa88e09ffbc65c37fe468bbc5ea3580971ccfdb..93e1f066955b7b3fde50fa965346e028dcf784af 100644
--- a/gdbserver/target.h
+++ b/gdbserver/target.h
@@ -94,9 +94,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 49f0ed3b1f4d2b2ac1d1c194d3910e4918568241..a9dda19e707a8cfe2b2209b95974025fff514e14 100644
--- a/gdbserver/win32-low.cc
+++ b/gdbserver/win32-low.cc
@@ -585,7 +585,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;
@@ -600,7 +600,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 daed16a6ae6a097ad4383c7ebe8b3cd3752b5eae..5c7f195e8c4eaa7568d457f17c1f4802192100c6 100644
--- a/gdbserver/win32-low.h
+++ b/gdbserver/win32-low.h
@@ -103,7 +103,7 @@ class win32_process_target : public process_stratum_target
   int create_inferior (const char *program,
 		       const std::vector<char *> &program_args) override;
 
-  int attach (unsigned long pid) override;
+  int attach (int pid) override;
 
   int kill (process_info *proc) override;
 

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 34/47] gdb: do not create a thread after a process event.
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (32 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 33/47] gdbserver: adjust pid after the target attaches Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 35/47] gdb, ze: on a whole process stop, mark all threads as not_resumed Tankut Baris Aktemur
                   ` (13 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

From: Natalia Saiapova <natalia.saiapova@intel.com>

When an event occurs and GDB cannot find a thread which corresponds to
the event ptid, it creates a new thread.  This patch adds a check that
if the eventing PTID is PID we skip creating the new thread.  Instead try
to find a corresponding inferior and take its first non exited thread for
the event context.  That prevents creating dummy threads.
---
 gdb/infrun.c | 21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/gdb/infrun.c b/gdb/infrun.c
index 8cee2c6e4c59c70f49c350da9346930f525b7da9..07d33c11f64a51d13e041f838aea0ae379d1720f 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -6193,7 +6193,26 @@ handle_inferior_event (struct execution_control_state *ecs)
       ecs->event_thread = ecs->target->find_thread (ecs->ptid);
       /* If it's a new thread, add it to the thread database.  */
       if (ecs->event_thread == nullptr)
-	ecs->event_thread = add_thread (ecs->target, ecs->ptid);
+	{
+	  /* Do not create a thread if the event is for the whole process.  */
+	  if (!ecs->ptid.is_pid ())
+	    ecs->event_thread = add_thread (ecs->target, ecs->ptid);
+	  else
+	    {
+	      inferior *inf = find_inferior_pid (ecs->target,
+						 ecs->ptid.pid ());
+	      gdb_assert (inf != nullptr);
+	      ecs->event_thread = any_live_thread_of_inferior (inf);
+	      /* If we end up here with no thread, that means that
+		 the eventing process has no non-exited threads.
+		 This situation is unexpected -- we need the thread's
+		 context.  */
+	      gdb_assert (ecs->event_thread != nullptr);
+	      /* Now that we have picked a representative thread,
+		 adjust the event ptid, too.  */
+	      ecs->ptid = ecs->event_thread->ptid;
+	    }
+	}
 
       /* Disable range stepping.  If the next step request could use a
 	 range, this will be end up re-enabled then.  */

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 35/47] gdb, ze: on a whole process stop, mark all threads as not_resumed
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (33 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 34/47] gdb: do not create a thread after a process event Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 36/47] gdb, dwarf, ze: add DW_OP_INTEL_regval_bits Tankut Baris Aktemur
                   ` (12 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

From: Klaus Gerlicher <klaus.gerlicher@intel.com>

In non-stop mode, when gdbserver sends a unavailable notification via a
library load stop-reply, set all non-exited threads to non_resumed
state in case it's a whole process stop-reply.
---
 gdb/remote.c | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/gdb/remote.c b/gdb/remote.c
index c74f6451b6c1f2a1c2dbcc51c70d8fecc3d82b0e..e9a8ded65cca34c2554173db6ed4d085f2335ee5 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -8652,11 +8652,18 @@ remote_target::process_stop_reply (stop_reply_up stop_reply,
 	    }
 	}
 
-      if (!target_is_non_stop_p ())
+      if (!target_is_non_stop_p () || ptid.is_pid ())
 	{
 	  /* If the target works in all-stop mode, a stop-reply indicates that
-	     all the target's threads stopped.  */
-	  for (thread_info *tp : all_non_exited_threads (this))
+	     all the target's threads stopped.
+
+	     In non-stop mode, a process-wide stop-reply indicates that
+	     all the threads of that process stopped.  */
+	  ptid_t filter_ptid = (!target_is_non_stop_p ()
+				? minus_one_ptid
+				: ptid);
+
+	  for (thread_info *tp : all_non_exited_threads (this, filter_ptid))
 	    get_remote_thread_info (tp)->set_not_resumed ();
 	}
     }

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 36/47] gdb, dwarf, ze: add DW_OP_INTEL_regval_bits
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (34 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 35/47] gdb, ze: on a whole process stop, mark all threads as not_resumed Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 37/47] gdbserver: allow configuring for a heterogeneous target Tankut Baris Aktemur
                   ` (11 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

From: Markus Metzger <markus.t.metzger@intel.com>

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  | 36 ++++++++++++++++++++++++++++++++++++
 gdb/dwarf2/expr.h  |  5 +++++
 gdb/dwarf2/loc.c   |  2 ++
 include/dwarf2.def |  4 ++++
 5 files changed, 53 insertions(+)

diff --git a/binutils/dwarf.c b/binutils/dwarf.c
index e8425b98d3660d531a0b4bff042fca0665a96f1b..fc3ecc3ab25db7c9810e36cf0219ac57fa8c204e 100644
--- a/binutils/dwarf.c
+++ b/binutils/dwarf.c
@@ -1703,6 +1703,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 5ad17eaaaff89b2d7f6f678e6e5b7e73e320f81c..96c6b258f263ad7a73599d631d24e03de06fa95f 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -862,6 +862,28 @@ dwarf_expr_context::read_mem (gdb_byte *buf, CORE_ADDR addr,
 
 /* 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 ULONGEST 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, regbuf);
+
+  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,
@@ -2330,6 +2352,20 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	  }
 	  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 b02cc53164065433d14d472b77a5a337125d10b9..8ecc4f130da1d6ba52dfc2cbb857d3ab0137c3fe 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -252,6 +252,11 @@ struct dwarf_expr_context
      but with the address being 0.  In this situation, we arrange for
      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);
 };
 
 /* Return the value of register number REG (a DWARF register number),
diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index 989d33901e01070d05c1009cdd80db60af4f0b2f..bd0848da452ed76548c60dee501239fc8e37be7f 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -2087,6 +2087,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;
 
@@ -3344,6 +3345,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 66c7fa1220f82f536617222545f7b9590626808d..4c2e27a155d833b53a8c43c34bf59f1efe32f5b1 100644
--- a/include/dwarf2.def
+++ b/include/dwarf2.def
@@ -688,6 +688,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.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 37/47] gdbserver: allow configuring for a heterogeneous target
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (35 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 36/47] gdb, dwarf, ze: add DW_OP_INTEL_regval_bits Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 38/47] gdbserver, ze, intelgt: introduce ze-low and intel-ze-low targets Tankut Baris Aktemur
                   ` (10 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

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 ffc84948317b2df667b75d5758bd67968c852967..db3f5631a417d3706829cfd8206ccf08c872a6c1 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.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 38/47] gdbserver, ze, intelgt: introduce ze-low and intel-ze-low targets
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (36 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 37/47] gdbserver: allow configuring for a heterogeneous target Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 39/47] testsuite, sycl: add SYCL support Tankut Baris Aktemur
                   ` (9 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

From: Markus Metzger <markus.t.metzger@intel.com>

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

For Level-Zero based devices, we model hardware threads.  There is one
GDB thread for each hardware thread on the device.

We distinguish RUNNING and UNAVAILABLE thread execution states.  They
are pretty similar but UNAVAILABLE means that we have tried to stop
the thread and failed, whereas RUNNING means we have resumed the
thread and since not tried to interact with it.

On attach to PID, attach to that PID on all supported devices and
create target descriptions and a process for each device.  This
version does 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
  ...
  [SYCL] Using device: [Intel(R) Arc(TM) A750 Graphics] from [Intel(R) 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 remote | gdbserver-ze --attach - 16458
  Remote debugging using | gdbserver-ze --attach - 16458
  Attached; given pid = 16458, updated to 1
  Remote debugging using stdio
  <unavailable> in ?? ()
  (gdb)

  # For "continue" to conveniently resume both inferiors,
  # set the 'schedule-multi' mode.  Then define a breakpoint
  # inside the kernel and resume to hit that BP.

  (gdb) set schedule-multiple on
  (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.

When GDB sends an interrupt, some threads may turn out to be
unavailable.  From GDB's PoV, those threads are not running.  However,
they may emit events.  In all-stop mode, those events are not fetched
until GDB resumes the threads, due to the synchronous communication
model.  In non-stop mode, however, the events may be fetched.  When
they are sent to GDB, they can cause confusion, in particular when GDB
commits resume requests.  To prevent a state mismatch, we hold events
that arise from unavailable threads whose resume state is 'stop'.
Once GDB sends resume requests for these threads, we unleash the
event.

Co-authored-by: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Co-authored-by: Natalia Saiapova <natalia.saiapova@intel.com>

Cc: <config-patches@gnu.org>
Cc: <binutils@sourceware.org>
---
 config.sub                  |    4 +-
 gdbserver/Makefile.in       |    4 +-
 gdbserver/config.in         |    6 +
 gdbserver/configure         |  500 ++++++++
 gdbserver/configure.ac      |   18 +
 gdbserver/configure.srv     |    4 +
 gdbserver/gdbthread.h       |    2 +-
 gdbserver/intelgt-ze-low.cc | 1041 +++++++++++++++
 gdbserver/ze-low.cc         | 2995 +++++++++++++++++++++++++++++++++++++++++++
 gdbserver/ze-low.h          |  492 +++++++
 10 files changed, 5062 insertions(+), 4 deletions(-)

diff --git a/config.sub b/config.sub
index 63ff958ec125e543674e9b261d5e5bb2fa749c4e..3b31f61817bd804ec89fc525a014365ae528d651 100755
--- a/config.sub
+++ b/config.sub
@@ -1769,7 +1769,7 @@ case $os in
 	     | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
 	     | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \
 	     | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr* \
-	     | fiwix* | mlibc* | cos* | mbr* | ironclad* )
+	     | fiwix* | mlibc* | cos* | mbr* | ironclad* | ze*)
 		;;
 	# This one is extra strict with allowed versions
 	sco3.2v2 | sco3.2v[4-9]* | sco5v6*)
@@ -1830,7 +1830,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-relibc*- | linux-uclibc*- | linux-ze*- )
 		;;
 	uclinux-uclibc*- )
 		;;
diff --git a/gdbserver/Makefile.in b/gdbserver/Makefile.in
index 365dcf23650476305dacc421c426fe54f4003301..75a5e685291af2207707ed6565f9ecbdfc2bc6b2 100644
--- a/gdbserver/Makefile.in
+++ b/gdbserver/Makefile.in
@@ -108,6 +108,8 @@ GDBSUPPORT = $(GDBSUPPORT_BUILDDIR)/libgdbsupport.a
 ustlibs = @ustlibs@
 ustinc = @ustinc@
 
+LIBZE_LOADER = @LIBZE_LOADER@
+
 # gnulib
 GNULIB_PARENT_DIR = ..
 include $(GNULIB_PARENT_DIR)/gnulib/Makefile.gnulib.inc
@@ -372,7 +374,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 65f9ff6e6470369ce8487e0ea3bdb186f323f7b1..28a49ef5ac97d5f7f538cf74a71304af27d21cbe 100644
--- a/gdbserver/config.in
+++ b/gdbserver/config.in
@@ -158,9 +158,15 @@
 /* Define to 1 if you have the `dl' library (-ldl). */
 #undef HAVE_LIBDL
 
+/* Define if you have the igfxdbg library. */
+#undef HAVE_LIBIGFXDBG
+
 /* Define if you have the ipt library. */
 #undef HAVE_LIBIPT
 
+/* Define if you have the ze_loader library. */
+#undef HAVE_LIBZE_LOADER
+
 /* Define if you have the xxhash library. */
 #undef HAVE_LIBXXHASH
 
diff --git a/gdbserver/configure b/gdbserver/configure
index 32980e5017edf88ae99d460e92787daadb0eddae..02fdfaaf803630849e35ffca79640471d5004bd5 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
@@ -785,6 +788,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
@@ -1460,6 +1465,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
@@ -14805,6 +14813,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 6e3a73a58d53b1bd005bd8d452c118a0835752b6..fd90ea08c21ed812ce38c4a9f3f0b93596ee68c8 100644
--- a/gdbserver/configure.ac
+++ b/gdbserver/configure.ac
@@ -458,6 +458,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 db3f5631a417d3706829cfd8206ccf08c872a6c1..98330540c8219d817768fbdd9e0b42ed6f79ed4f 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -147,6 +147,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_linux_regsets=yes
diff --git a/gdbserver/gdbthread.h b/gdbserver/gdbthread.h
index d7b5bc3e1538bde8eafd0f3253706924d7330a21..e23d60ae1ae944dbe2561ffc35a85b808d681dc0 100644
--- a/gdbserver/gdbthread.h
+++ b/gdbserver/gdbthread.h
@@ -46,7 +46,7 @@ struct thread_info : public intrusive_list_node<thread_info>
   void set_regcache (struct regcache *regcache)
   { m_regcache = 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 0000000000000000000000000000000000000000..27efe6560e93d1ff9531c1437f59e1fe16459652
--- /dev/null
+++ b/gdbserver/intelgt-ze-low.cc
@@ -0,0 +1,1041 @@
+/* Target interface for Intel GT based on Level-Zero for gdbserver.
+
+   Copyright (C) 2020-2024 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>
+
+
+/* FIXME make into a target method?  */
+int using_threads = 1;
+
+/* Convenience macros.  */
+
+#define dprintf(...)						\
+  do								\
+    {								\
+      if (debug_threads)					\
+	{							\
+	  debug_printf ("%s: ", __FUNCTION__);			\
+	  debug_printf (__VA_ARGS__);				\
+	  debug_printf ("\n");					\
+	}							\
+    }								\
+  while (0)
+
+
+/* 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);
+
+  char type_name[20];
+  snprintf (type_name, sizeof (type_name), "uint%u", elemsize);
+  tdesc_type *type = tdesc_named_type (feature, type_name);
+
+  if (elemsize == bitsize)
+    return type->name.c_str ();
+
+  uint32_t elements = bitsize / elemsize;
+  snprintf (type_name, sizeof (type_name), "vector%ux%u", elements,
+	    elemsize);
+  tdesc_type *vector
+    = tdesc_create_vector (feature, type_name, type, elements);
+
+  return vector->name.c_str ();
+}
+
+/* Add a (uniform) register set to FEATURE.  */
+
+static void
+intelgt_add_regset (tdesc_feature *feature, long &regnum,
+		    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,
+};
+
+/* Return CR0.SUBREG in REGCACHE.  */
+
+static uint32_t
+intelgt_read_cr0 (regcache *regcache, int subreg)
+{
+  int cr0regno = find_regno (regcache->tdesc, "cr0");
+  int cr0size = register_size (regcache->tdesc, cr0regno);
+  uint32_t cr0[16];
+  gdb_assert (cr0size <= sizeof (cr0));
+  gdb_assert (cr0size >= sizeof (cr0[0]) * (subreg + 1));
+  collect_register (regcache, cr0regno, cr0);
+
+  enum register_status cr0status = regcache->get_register_status (cr0regno);
+  switch (cr0status)
+    {
+    case REG_VALID:
+      return cr0[subreg];
+
+    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.SUBREG in REGCACHE.  */
+
+static void
+intelgt_write_cr0 (regcache *regcache, int subreg, uint32_t value)
+{
+  int cr0regno = find_regno (regcache->tdesc, "cr0");
+  int cr0size = register_size (regcache->tdesc, cr0regno);
+  uint32_t cr0[16];
+  gdb_assert (cr0size <= sizeof (cr0));
+  gdb_assert (cr0size >= sizeof (cr0[0]) * (subreg + 1));
+  collect_register (regcache, cr0regno, cr0);
+
+  enum register_status cr0status = regcache->get_register_status (cr0regno);
+  switch (cr0status)
+    {
+    case REG_VALID:
+      cr0[subreg] = value;
+      supply_register (regcache, cr0regno, cr0);
+      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.SUBREG for TP.  */
+
+static uint32_t
+intelgt_read_cr0 (thread_info *tp, int subreg)
+{
+  struct regcache *regcache = get_thread_regcache (tp, /* fetch = */ true);
+  return intelgt_read_cr0 (regcache, subreg);
+}
+
+/* Write VALUE into CR0.SUBREG for TP.  */
+
+static void
+intelgt_write_cr0 (thread_info *tp, int subreg, uint32_t value)
+{
+  struct regcache *regcache = get_thread_regcache (tp, /* fetch = */ true);
+  intelgt_write_cr0 (regcache, subreg, value);
+}
+
+/* Return a human-readable device UUID string.  */
+
+static std::string
+device_uuid_str (const uint8_t uuid[], size_t size)
+{
+  std::stringstream sstream;
+  for (int i = size - 1; i >= 0; --i)
+    sstream << std::hex << std::setfill ('0') << std::setw (2)
+	    << static_cast<int> (uuid[i]);
+
+  return sstream.str ();
+}
+
+/* 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 (thread_info *, 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 (thread_info *tp, CORE_ADDR pc,
+		 gdb::array_view<gdb_byte> buffer);
+
+  bool is_at_breakpoint (thread_info *tp) override;
+  bool is_at_eot (thread_info *tp);
+
+  bool erratum_18020355813 (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_properties_t &device,
+		   const zet_debug_regset_properties_t &regprop,
+		   long &regnum, ze_regset_info_t &regsets,
+		   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)
+{
+  uint32_t ip = intelgt_read_cr0 (regcache, 2);
+  uint64_t isabase;
+  collect_register_by_name (regcache, "isabase", &isabase);
+
+  if (UINT32_MAX < ip)
+    warning (_("IP '0x%" PRIx32 "' outside of ISA range."), ip);
+
+  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));
+
+  intelgt_write_cr0 (regcache, 2, (uint32_t) pc);
+}
+
+bool
+intelgt_ze_target::is_device_supported
+  (const ze_device_properties_t &properties,
+   const std::vector<zet_debug_regset_properties_t> &regset_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 &regprop : regset_properties)
+    {
+      if (regprop.count < 1)
+	{
+	  warning (_("Ignoring empty regset %u in %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:
+	  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 1.  */
+	  if ((regprop.version == 0) && (regprop.count >= 5))
+	    have_isabase = true;
+	  else
+	    warning (_("Ignoring unknown SBA regset version %u in %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> &regset_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);
+
+  std::string device_uuid = device_uuid_str (
+    dinfo->properties.uuid.id, sizeof (dinfo->properties.uuid.id));
+  const uint32_t total_cores = (properties.numSlices
+				* properties.numSubslicesPerSlice
+				* properties.numEUsPerSubslice);
+  const uint32_t total_threads = (total_cores * properties.numThreadsPerEU);
+
+  tdesc_device *device_info = new tdesc_device ();
+  device_info->vendor_id = properties.vendorId;
+  device_info->target_id = properties.deviceId;
+  device_info->name = properties.name;
+  device_info->pci_slot = string_printf ("%02" PRIx32 ":%02" PRIx32
+					 ".%" PRId32,
+					 pci_properties.address.bus,
+					 pci_properties.address.device,
+					 pci_properties.address.function);
+  device_info->uuid = device_uuid;
+  device_info->total_cores = total_cores;
+  device_info->total_threads = total_threads;
+
+  if (properties.flags & ZE_DEVICE_PROPERTY_FLAG_SUBDEVICE)
+    device_info->subdevice_id = properties.subdeviceId;
+
+  set_tdesc_device_info (tdesc.get (), device_info);
+
+  long regnum = 0;
+  for (const zet_debug_regset_properties_t &regprop : regset_properties)
+    add_regset (tdesc.get (), properties, 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 (thread_info *tp, gdb_signal &signal)
+{
+  ze_device_thread_t thread = ze_thread_id (tp);
+  uint32_t cr0[3] = {
+    intelgt_read_cr0 (tp, 0),
+    intelgt_read_cr0 (tp, 1),
+    intelgt_read_cr0 (tp, 2)
+  };
+
+  dprintf ("thread %s (%s) stopped, cr0.0=%" PRIx32 ", .1=%" PRIx32
+	   " [ %s%s%s%s%s%s], .2=%" PRIx32 ".", tp->id.to_string ().c_str (),
+	   ze_thread_id_str (thread).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]);
+
+  if ((cr0[1] & (1 << INTELGT_CR0_1_PAGEFAULT_STATUS)) != 0)
+    {
+      cr0[1] &= ~(1 << INTELGT_CR0_1_PAGEFAULT_STATUS);
+      intelgt_write_cr0 (tp, 1, cr0[1]);
+
+      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 (tp, 1, cr0[1]);
+
+      /* 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.  */
+      const ze_thread_info *zetp = ze_thread (tp);
+      gdb_assert (zetp != nullptr);
+
+      switch (zetp->resume_state)
+	{
+	case ZE_THREAD_RESUME_STEP:
+	  signal = GDB_SIGNAL_TRAP;
+	  return TARGET_STOPPED_BY_SINGLE_STEP;
+
+	case ZE_THREAD_RESUME_RUN:
+	case ZE_THREAD_RESUME_NONE:
+	  /* On some devices, we may get spurious breakpoint exceptions.  */
+	  if (erratum_18020355813 (tp))
+	    {
+	      ze_device_thread_t zeid = ze_thread_id (tp);
+
+	      dprintf ("applying #18020355813 workaround for thread "
+		       "%s (%s)", tp->id.to_string ().c_str (),
+		       ze_thread_id_str (zeid).c_str ());
+
+	      signal = GDB_SIGNAL_0;
+	      return TARGET_STOPPED_BY_NO_REASON;
+	    }
+
+	  [[fallthrough]];
+	case ZE_THREAD_RESUME_STOP:
+	  signal = GDB_SIGNAL_TRAP;
+	  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 (tp, 1, cr0[1]);
+
+      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 (tp, 1, cr0[1]);
+
+      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 (tp, 1, cr0[1]);
+
+      signal = GDB_SIGNAL_TRAP;
+      return TARGET_STOPPED_BY_NO_REASON;
+    }
+
+  signal = GDB_SIGNAL_UNKNOWN;
+  return TARGET_STOPPED_BY_NO_REASON;
+}
+
+int
+intelgt_ze_target::read_inst (thread_info *tp, CORE_ADDR pc,
+			      gdb::array_view<gdb_byte> buffer)
+{
+  gdb_assert (buffer.size () >= intelgt::MAX_INST_LENGTH);
+
+  int status = read_memory (tp, pc, buffer.data (), intelgt::MAX_INST_LENGTH);
+  if (status == 0)
+    return intelgt::MAX_INST_LENGTH;
+
+  status = read_memory (tp, pc, buffer.data (), intelgt::COMPACT_INST_LENGTH);
+  if (status > 0)
+    return status;
+
+  if (!intelgt::is_compacted_inst (buffer))
+    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);
+
+  gdb_byte inst[intelgt::MAX_INST_LENGTH];
+  int status = read_inst (tp, pc, inst);
+  if (status < 0)
+    return false;
+
+  return intelgt::has_breakpoint (inst);
+}
+
+bool
+intelgt_ze_target::is_at_eot (thread_info *tp)
+{
+  regcache *regcache = get_thread_regcache (tp, /* fetch = */ true);
+  CORE_ADDR pc = read_pc (regcache);
+
+  gdb_byte inst[intelgt::MAX_INST_LENGTH];
+  int status = read_inst (tp, 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;
+    }
+
+  uint8_t opc = inst[0] & intelgt::OPC_MASK;
+  switch (opc)
+    {
+    case intelgt::OPC_SEND:
+    case intelgt::OPC_SENDC:
+      return intelgt::get_inst_bit (inst, intelgt::CTRL_EOT);
+
+    default:
+      return false;
+    }
+}
+
+/* Return whether erratum #18020355813 applies.  */
+
+bool
+intelgt_ze_target::erratum_18020355813 (thread_info *tp)
+{
+  const process_info *process = tp->process ();
+  if (process == nullptr)
+    {
+      ze_device_thread_t zeid = ze_thread_id (tp);
+
+      warning (_("error getting process for thread %s (%s)"),
+	       tp->id.to_string ().c_str (),
+	       ze_thread_id_str (zeid).c_str ());
+      return false;
+    }
+
+  process_info_private *zeinfo = process->priv;
+  gdb_assert (zeinfo != nullptr);
+
+  /* We may not have a device if we got detached.  */
+  ze_device_info *device = zeinfo->device;
+  if (device == nullptr)
+    return false;
+
+  /* The erratum only applies to Intel devices.  */
+  if (device->properties.vendorId != 0x8086)
+    return false;
+
+  /* The erratum only applies to a range of devices.  */
+  switch (device->properties.deviceId)
+    {
+    case 0x4f80:
+    case 0x4f81:
+    case 0x4f82:
+    case 0x4f83:
+    case 0x4f84:
+    case 0x4f85:
+    case 0x4f86:
+    case 0x4f87:
+    case 0x4f88:
+    case 0x56a0:
+    case 0x56a1:
+    case 0x56a2:
+    case 0x5690:
+    case 0x5691:
+    case 0x5692:
+    case 0x56c0:
+    case 0x56c1:
+    case 0x56c2:
+    case 0x56a3:
+    case 0x56a4:
+    case 0x56a5:
+    case 0x56a6:
+    case 0x5693:
+    case 0x5694:
+    case 0x5695:
+    case 0x5696:
+    case 0x5697:
+    case 0x56b0:
+    case 0x56b1:
+    case 0x56b2:
+    case 0x56b3:
+    case 0x56ba:
+    case 0x56bb:
+    case 0x56bc:
+    case 0x56bd:
+
+    case 0x0bd0:
+    case 0x0bd4:
+    case 0x0bd5:
+    case 0x0bd6:
+    case 0x0bd7:
+    case 0x0bd8:
+    case 0x0bd9:
+    case 0x0bda:
+    case 0x0bdb:
+    case 0x0b69:
+    case 0x0b6e:
+      break;
+
+    default:
+      return false;
+    }
+
+  regcache *regcache = get_thread_regcache (tp, /* fetch = */ true);
+  CORE_ADDR pc = read_pc (regcache);
+
+  gdb_byte inst[intelgt::MAX_INST_LENGTH];
+  int status = read_inst (tp, 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;
+    }
+
+  /* The erratum applies to instructions without breakpoint control.  */
+  return !intelgt::has_breakpoint (inst);
+}
+
+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);
+  uint32_t cr0[3] = {
+    intelgt_read_cr0 (regcache, 0),
+    intelgt_read_cr0 (regcache, 1),
+    intelgt_read_cr0 (regcache, 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[1] |= (1 << INTELGT_CR0_1_BREAKPOINT_STATUS);
+	  break;
+	}
+
+      /* At EOT, the thread dispatch ends and the thread becomes idle.
+
+	 There's no point in requesting a single-step exception but we
+	 need to inject an event to tell GDB that the step completed.  */
+      zetp->exec_state = ZE_THREAD_STATE_UNAVAILABLE;
+      zetp->waitstatus.set_unavailable ();
+
+      [[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, 0, cr0[0]);
+  intelgt_write_cr0 (regcache, 1, cr0[1]);
+  intelgt_write_cr0 (regcache, 2, cr0[2]);
+
+  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_properties_t &device,
+			       const zet_debug_regset_properties_t &regprop,
+			       long &regnum, ze_regset_info_t &regsets,
+			       expedite_t &expedite)
+{
+  tdesc_feature *feature = nullptr;
+
+  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);
+
+      expedite.push_back ("r0");
+
+      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);
+
+      expedite.push_back ("sr0");
+      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",
+	      "scrbase",
+	      "scrbase2",
+	      nullptr
+	    };
+	    int reg = 0;
+	    for (; (reg < regprop.count) && (sbaregs[reg] != nullptr); ++reg)
+	      {
+		if ((strcmp (sbaregs[reg], "genstbase") == 0)
+		    || (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 0000000000000000000000000000000000000000..12d73775f91b642d0f8d940acec0053d3c5c5d3e
--- /dev/null
+++ b/gdbserver/ze-low.cc
@@ -0,0 +1,2995 @@
+/* Target interface for Level-Zero based targets for gdbserver.
+   See https://github.com/oneapi-src/level-zero.git.
+
+   Copyright (C) 2020-2024 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> /* For snprintf.  */
+#include <thread>
+#include <utility>
+#include <algorithm>
+#include <set>
+
+#ifndef USE_WIN32API
+#  include <signal.h>
+#  include <fcntl.h>
+#endif
+
+
+/* Convenience macros.  */
+
+#define dprintf(...)						\
+  do								\
+    {								\
+      if (debug_threads)					\
+	{							\
+	  debug_printf ("%s: ", __FUNCTION__);			\
+	  debug_printf (__VA_ARGS__);				\
+	  debug_printf ("\n");					\
+	}							\
+    }								\
+  while (0)
+
+#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 a human-readable process state string.  */
+
+static const char *
+ze_process_state_str (ze_process_state state)
+{
+  switch (state)
+    {
+    case ZE_PROCESS_VISIBLE:
+      return "visible";
+
+    case ZE_PROCESS_HIDDEN:
+      return "hidden";
+    }
+
+  return "unknown";
+}
+
+/* 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.  */
+
+static ze_device_info *
+ze_thread_device (const thread_info *thread)
+{
+  if (thread == nullptr)
+    return nullptr;
+
+  return ze_process_device (thread->process ());
+}
+
+/* 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;
+}
+
+/* Call FUNC for each thread on DEVICE matching ID.  */
+
+template <typename Func>
+static void
+for_each_thread (const ze_device_info &device, ze_device_thread_t id,
+		 Func func)
+{
+  device.process->for_each_thread ([id, func] (thread_info *tp)
+    {
+      ze_device_thread_t tid = ze_thread_id (tp);
+      if (ze_device_thread_in (tid, id))
+	func (tp);
+    });
+}
+
+/* Add a process for DEVICE.  */
+
+static process_info *
+ze_add_process (ze_device_info *device, ze_process_state state)
+{
+  gdb_assert (device != nullptr);
+
+  process_info *process = add_process (device->ordinal, 1);
+  process->priv = new process_info_private (device, state);
+  process->tdesc = device->tdesc.get ();
+  device->process = process;
+
+  /* Enumerate threads on the device we attached to.
+
+     We debug the entire device so we can enumerate all threads at once.  They
+     will be idle some of the time and we won't be able to interact with them.
+     When work gets submitted to the device, the thread dispatcher will
+     distribute the work onto device threads.
+
+     The alternative of only representing threads that are currently executing
+     work would be too intrusive as we'd need to stop each thread on every
+     dispatch.  */
+  long tid = 0;
+  uint32_t slice, sslice, eu, thread;
+  const ze_device_properties_t &properties = device->properties;
+  for (slice = 0; slice < properties.numSlices; ++slice)
+    for (sslice = 0; sslice < properties.numSubslicesPerSlice; ++sslice)
+      for (eu = 0; eu < properties.numEUsPerSubslice; ++eu)
+	for (thread = 0; thread < properties.numThreadsPerEU; ++thread)
+	  {
+	    /* We use the device ordinal as process id.  */
+	    ptid_t ptid = ptid_t ((int) device->ordinal, ++tid, 0l);
+
+	    /* We can only support that many threads.  */
+	    if (tid < 0)
+	      error (_("Too many threads on device %lu: %s."),
+		     device->ordinal, properties.name);
+
+	    /* Storing the 128b device thread id in the private data.  We might
+	       want to extend ptid_t and put it there so GDB can show it to the
+	       user.  */
+	    ze_thread_info *zetp = new ze_thread_info {};
+	    zetp->id.slice = slice;
+	    zetp->id.subslice = sslice;
+	    zetp->id.eu = eu;
+	    zetp->id.thread = thread;
+
+	    /* Assume threads are running until we hear otherwise.  */
+	    zetp->exec_state = ZE_THREAD_STATE_RUNNING;
+
+	    process->add_thread (ptid, zetp);
+	  }
+
+  device->nthreads = tid;
+  device->nresumed = tid;
+
+  dprintf ("process %d (%s) with %ld threads created for device %lu: %s.",
+	   (int) device->ordinal, ze_process_state_str (state), tid,
+	   device->ordinal, properties.name);
+
+  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);
+}
+
+/* Show PROCESS.  */
+
+static void
+ze_show_process (process_info *process)
+{
+  gdb_assert (process != nullptr);
+  process_info_private *priv = process->priv;
+
+  gdb_assert (priv != nullptr);
+  switch (priv->state)
+    {
+    case ZE_PROCESS_VISIBLE:
+      return;
+
+    case ZE_PROCESS_HIDDEN:
+      /* FIXME: report state change
+
+	 Set priv->status and report the new visibility to GDB.  */
+      priv->state = ZE_PROCESS_VISIBLE;
+      return;
+    }
+
+  internal_error (_("unknown process state"));
+}
+
+/* Hide PROCESS.  */
+
+static void
+ze_hide_process (process_info *process)
+{
+  gdb_assert (process != nullptr);
+  process_info_private *priv = process->priv;
+
+  gdb_assert (priv != nullptr);
+  switch (priv->state)
+    {
+    case ZE_PROCESS_HIDDEN:
+      return;
+
+    case ZE_PROCESS_VISIBLE:
+      /* FIXME: report state change
+
+	 Set priv->status and report the new visibility to GDB.  */
+      priv->state = ZE_PROCESS_HIDDEN;
+      return;
+    }
+
+  internal_error (_("unknown process state"));
+}
+
+/* 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);
+
+  if (device->session != nullptr)
+    error (_("Already attached to %s."), device->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);
+
+      return ze_add_process (device, ZE_PROCESS_HIDDEN);
+
+    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";
+      return sstream.str ();
+
+    case ZET_DEBUG_EVENT_TYPE_DETACHED:
+      sstream << "detached, reason="
+	      << ze_detach_reason_str (event.info.detached.reason);
+      return sstream.str ();
+
+    case ZET_DEBUG_EVENT_TYPE_PROCESS_ENTRY:
+      sstream << "process entry";
+      return sstream.str ();
+
+    case ZET_DEBUG_EVENT_TYPE_PROCESS_EXIT:
+      sstream << "process exit";
+      return sstream.str ();
+
+    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
+	      << ", need-ack: "
+	      << ((event.flags & ZET_DEBUG_EVENT_FLAG_NEED_ACK) != 0);
+      return sstream.str ();
+
+    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;
+      return sstream.str ();
+
+    case ZET_DEBUG_EVENT_TYPE_THREAD_STOPPED:
+      sstream << "thread stopped, thread="
+	      << ze_thread_id_str (event.info.thread.thread);
+      return sstream.str ();
+
+    case ZET_DEBUG_EVENT_TYPE_THREAD_UNAVAILABLE:
+      sstream << "thread unavailable, thread="
+	      << ze_thread_id_str (event.info.thread.thread);
+      return sstream.str ();
+    }
+
+  sstream << "unknown, code=" << event.type;
+  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 resume state.  */
+
+static enum ze_thread_resume_state_t
+ze_resume_state (const thread_info *tp)
+{
+  const ze_thread_info *zetp = ze_thread (tp);
+  if (zetp == nullptr)
+    return ZE_THREAD_RESUME_NONE;
+
+  return zetp->resume_state;
+}
+
+/* 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_HELD)
+	  || (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:
+    case TARGET_WAITKIND_UNAVAILABLE:
+      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_TRAP))
+	return (zetp->resume_state == ZE_THREAD_RESUME_STOP);
+
+      /* If this thread stopped spuriously, it is not interesting.  */
+      if ((zetp->stop_reason == TARGET_STOPPED_BY_NO_REASON)
+	  && (zetp->waitstatus.sig () == GDB_SIGNAL_0))
+	return false;
+
+      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 (process_info *process, zet_debug_detach_reason_t reason)
+{
+  gdb_assert (process != nullptr);
+
+  /* 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 mark all threads of that process exited.  We already
+     use the process exit wait status as all threads will exit together.
+
+     In the second step, when one of those threads gets selected for reporting
+     its event, we will remove the process as part of the reporting flow.  */
+
+  process->for_each_thread ([reason] (thread_info *tp)
+    {
+      ze_thread_info *zetp = ze_thread (tp);
+      gdb_assert (zetp != nullptr);
+
+      zetp->waitstatus.set_exited ((int) reason);
+    });
+}
+
+/* Find the regset containing REGNO on DEVICE or throw if not found.  */
+
+static ze_regset_info
+ze_find_regset (const ze_device_info &device, long regno)
+{
+  for (const ze_regset_info &regset : device.regsets)
+    {
+      if (regno < regset.begin)
+	continue;
+
+      if (regset.end <= regno)
+	continue;
+
+      return regset;
+    }
+
+  error (_("No register %ld on %s."), regno, 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 &regset : device.regsets)
+    {
+      gdb_assert (regset.begin <= regset.end);
+      long lnregs = regset.end - regset.begin;
+
+      gdb_assert (lnregs < UINT32_MAX);
+      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)
+	{
+	case ZE_RESULT_SUCCESS:
+	case ZE_RESULT_ERROR_NOT_AVAILABLE:
+	  {
+	    size_t offset = 0;
+	    long 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;
+
+	default:
+	  warning (_("Error %x reading regset %" PRIu32 " for %s on %s."),
+		   status, regset.type, ze_thread_id_str (thread).c_str (),
+		   device.properties.name);
+
+	  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, long regno)
+{
+  ze_regset_info regset = ze_find_regset (device, regno);
+
+  gdb_assert (regset.begin <= regno);
+  long lrsno = regno - regset.begin;
+
+  gdb_assert (lrsno <= UINT32_MAX);
+  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;
+
+    case ZE_RESULT_ERROR_NOT_AVAILABLE:
+      supply_register (regcache, regno, nullptr);
+      break;
+
+    default:
+      warning (_("Error %x reading register %ld (regset %" PRIu32
+		 ") for %s on %s."), status, regno, regset.type,
+	       ze_thread_id_str (thread).c_str (), device.properties.name);
+      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 &regset : device.regsets)
+    {
+      if (!regset.is_writeable)
+	continue;
+
+      gdb_assert (regset.begin <= regset.end);
+      long lnregs = regset.end - regset.begin;
+
+      gdb_assert (lnregs < UINT32_MAX);
+      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 %s."),
+		 status, regset.type, ze_thread_id_str (thread).c_str (),
+		 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, long regno)
+{
+  ze_regset_info regset = ze_find_regset (device, regno);
+
+  if (!regset.is_writeable)
+    error (_("Writing read-only register %ld (regset %" PRIu32
+	     ") for %s on %s."), regno, regset.type,
+	   ze_thread_id_str (thread).c_str (), device.properties.name);
+
+  gdb_assert (regset.begin <= regno);
+  long lrsno = regno - regset.begin;
+
+  gdb_assert (lrsno <= UINT32_MAX);
+  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 %ld (regset %" PRIu32
+	       ") for %s on %s."), status,  regno, regset.type,
+	     ze_thread_id_str (thread).c_str (),
+	     device.properties.name);
+    }
+}
+
+/* Discard TP's regcache.  */
+
+static void
+ze_discard_regcache (thread_info *tp)
+{
+  regcache *regcache = get_thread_regcache (tp, /* fetch = */ false);
+  gdb_assert (regcache != nullptr);
+
+  regcache->registers_valid = 0;
+}
+
+/* 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));
+
+  /* When we get detached, we will remove the device but we will also mark
+     each thread exited.  We shouldn't try to resume them.  */
+  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:
+      device->nresumed++;
+      if (device->nresumed > device->nthreads)
+	{
+	  device->nresumed = device->nthreads;
+	  dprintf ("capping device %lu's nresumed at %ld (all)",
+		   device->ordinal, device->nthreads);
+	}
+      return true;
+
+    case ZE_THREAD_STATE_HELD:
+      gdb_assert_not_reached ("threads with 'held' state should "
+			      "have been turned into 'stopped'");
+
+    case ZE_THREAD_STATE_UNAVAILABLE:
+      device->nresumed++;
+      if (device->nresumed > device->nthreads)
+	{
+	  device->nresumed = device->nthreads;
+	  dprintf ("capping device %lu's nresumed at %ld (all)",
+		   device->ordinal, device->nthreads);
+	}
+
+      zetp->exec_state = ZE_THREAD_STATE_RUNNING;
+
+      /* Ignore resuming unavailable threads.  */
+      return false;
+
+    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);
+
+  /* When we get detached, we will remove the device but we will also mark
+     each thread exited.  We shouldn't try to stop them.  */
+  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_STOPPED:
+      /* We silently ignore already stopped threads.  */
+      return false;
+
+    case ZE_THREAD_STATE_HELD:
+      gdb_assert_not_reached ("threads with 'held' state should "
+			      "have been turned into 'stopped'");
+
+    case ZE_THREAD_STATE_PAUSED:
+      /* A paused thread is already stopped.  */
+      zetp->exec_state = ZE_THREAD_STATE_STOPPED;
+      return false;
+
+    case ZE_THREAD_STATE_UNAVAILABLE:
+    case ZE_THREAD_STATE_RUNNING:
+      return true;
+
+    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);
+}
+
+/* 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:
+      /* Ignore this if we're not modeling DEVICE as a process anymore.  */
+      if (device.process == nullptr)
+	break;
+
+      /* 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 %s."),
+	       ze_thread_id_str (thread).c_str (), device.properties.name);
+
+      /* Update our thread state to reflect the target.  */
+      for_each_thread (device, thread, [&] (thread_info *tp)
+	{
+	  ze_thread_info *zetp = ze_thread (tp);
+	  gdb_assert (zetp != nullptr);
+
+	  zetp->exec_state = ZE_THREAD_STATE_UNAVAILABLE;
+	  zetp->waitstatus.set_unavailable ();
+	});
+      break;
+
+    default:
+      error (_("Failed to resume %s on %s: %x."),
+	     ze_thread_id_str (thread).c_str (), 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, nresumed=%ld%s",
+	   device.ordinal, device.properties.name,
+	   ze_thread_id_str (thread).c_str (), device.nresumed,
+	   ((device.nresumed == device.nthreads) ? " (all)" : ""));
+
+  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 or unavailable.
+
+	 Assuming that our state tracking works, update non-stopped
+	 threads to reflect that.  */
+      for_each_thread (device, thread, [&] (thread_info *tp)
+	{
+	  if (ze_thread_stopped (tp))
+	    return;
+
+	  ze_thread_info *zetp = ze_thread (tp);
+	  gdb_assert (zetp != nullptr);
+
+	  zetp->exec_state = ZE_THREAD_STATE_UNAVAILABLE;
+	  zetp->waitstatus.set_unavailable ();
+	});
+	break;
+
+    default:
+      error (_("Failed to interrupt %s on %s: %x."),
+	     ze_thread_id_str (thread).c_str (), 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 * const 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 * const 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;
+    }
+
+  /* Check with the actual target implementation whether it supports this kind
+     of device.  */
+  if (!is_device_supported (properties, regsets))
+    {
+      dprintf ("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;
+
+  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;
+}
+
+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 %s: %x."),
+		 device.properties.name, status);
+	}
+
+      dprintf ("received event from device %lu: %s", device.ordinal,
+	       ze_event_str (event).c_str ());
+
+      switch (event.type)
+	{
+	case ZET_DEBUG_EVENT_TYPE_INVALID:
+	  break;
+
+	case ZET_DEBUG_EVENT_TYPE_DETACHED:
+	  {
+	    process_info *process = device.process;
+	    if (process != nullptr)
+	      ze_device_detached (process, event.info.detached.reason);
+
+	    /* We're detached, now.  */
+	    device.session = nullptr;
+	  }
+	  return nevents;
+
+	case ZET_DEBUG_EVENT_TYPE_PROCESS_ENTRY:
+	  ze_ack_event (device, event);
+	  ze_show_process (device.process);
+	  continue;
+
+	case ZET_DEBUG_EVENT_TYPE_PROCESS_EXIT:
+	  ze_ack_event (device, event);
+	  ze_hide_process (device.process);
+	  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, need_ack);
+
+	    /* 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);
+
+	    /* Loading a new module is a process event.  We do not want to
+	       overwrite other process events, however, as module loads
+	       can also be communicated as part of other events.  */
+	    process_info_private *zeproc = process->priv;
+	    gdb_assert (zeproc != nullptr);
+
+	    if (zeproc->waitstatus.kind () != TARGET_WAITKIND_IGNORE)
+	      continue;
+
+	    /* We use UNAVAILABLE rather than LOADED as the latter implies
+	       that the target has stopped.  */
+	    zeproc->waitstatus.set_unavailable ();
+	  }
+	  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 tid = 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, tid, [&] (thread_info *tp)
+	      {
+		/* Ignore threads we know to be stopped.
+
+		   We already analyzed the stop reason and probably
+		   destroyed it in the process.  */
+		if (ze_thread_stopped (tp))
+		  return;
+
+		/* Prevent underflowing.  */
+		if (device.nresumed > 0)
+		  device.nresumed--;
+
+		ze_thread_info *zetp = ze_thread (tp);
+		gdb_assert (zetp != nullptr);
+
+		/* Discard any registers we may have fetched while TP was
+		   unavailable.  */
+		ze_discard_regcache (tp);
+		try
+		  {
+		    gdb_signal signal = GDB_SIGNAL_0;
+
+		    /* If this is an unavailable thread with a 'stop'
+		       resume state, from GDB's point of view the
+		       thread was interrupted.  In all-stop mode, we
+		       keep the event held to not confuse GDB.
+
+		       Do the state update before get_stop_reason
+		       below, so that in case we access memory, we
+		       will do that using the right thread
+		       context.  */
+		    if (!non_stop
+			&& (zetp->exec_state == ZE_THREAD_STATE_UNAVAILABLE)
+			&& (zetp->resume_state == ZE_THREAD_RESUME_STOP))
+		      zetp->exec_state = ZE_THREAD_STATE_HELD;
+		    else
+		      zetp->exec_state = ZE_THREAD_STATE_STOPPED;
+
+		    target_stop_reason reason = get_stop_reason (tp, signal);
+
+		    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 (...)
+		  {
+		    zetp->exec_state = ZE_THREAD_STATE_UNAVAILABLE;
+		    zetp->waitstatus.set_unavailable ();
+		  }
+	      });
+
+	    dprintf ("device %lu's nresumed=%ld%s",
+		     device.ordinal, device.nresumed,
+		     ((device.nresumed == device.nthreads) ? " (all)" : ""));
+
+	    /* This is the response to an interrupt if TID is "all".  */
+	    if (ze_is_thread_id_all (tid))
+	      {
+		if (device.ninterrupts > 0)
+		  device.ninterrupts--;
+		else
+		  warning (_("ignoring spurious stop-all event on "
+			     "device %lu"), device.ordinal);
+	      }
+
+	    /* A thread event turns a process visible.  */
+	    if (nstopped > 0)
+	      ze_show_process (process);
+	  }
+	  continue;
+
+	case ZET_DEBUG_EVENT_TYPE_THREAD_UNAVAILABLE:
+	  {
+	    ze_device_thread_t tid = 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);
+
+	    for_each_thread (device, tid, [&] (thread_info *tp)
+	      {
+		/* 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;
+
+		/* Prevent underflowing.  */
+		if (device.nresumed > 0)
+		  device.nresumed--;
+
+		ze_thread_info *zetp = ze_thread (tp);
+		gdb_assert (zetp != nullptr);
+
+		zetp->exec_state = ZE_THREAD_STATE_UNAVAILABLE;
+		zetp->waitstatus.set_unavailable ();
+	      });
+
+	    dprintf ("device %lu's nresumed=%ld%s",
+		     device.ordinal, device.nresumed,
+		     ((device.nresumed == device.nthreads) ? " (all)" : ""));
+
+	    /* This is the response to an interrupt if TID is "all".  */
+	    if (ze_is_thread_id_all (tid))
+	      {
+		if (device.ninterrupts > 0)
+		  device.ninterrupts--;
+		else
+		  warning (_("ignoring spurious unavailable-all event on "
+			     "device %lu"), device.ordinal);
+	      }
+	  }
+	  continue;
+	}
+
+      /* We only get here if we have not processed EVENT.  */
+      warning (_("ignoring event '%s' on %s."),
+	       ze_event_str (event).c_str (),
+	       device.properties.name);
+
+      /* Acknowledge the ignored event so we don't get stuck.  */
+      ze_ack_event (device, event);
+    }
+}
+
+void
+ze_target::fetch_events_all_devices_no_resumed ()
+{
+  uint64_t nresumed = 0;
+  do {
+    nresumed = 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);
+	nresumed += device->nresumed;
+      }
+  }
+  while (nresumed != 0);
+}
+
+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::vector<char *> &argv)
+{
+  /* 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 ());
+
+  /* In all-stop mode above, we interrupted the devices.  Now we make sure
+     they come to a stop state.  So, we fetch events until no device has any
+     resumed threads left.  There might be low priority events (e.g.
+     'module load', 'process entry') we should fetch before fetching higher
+     priority events in the subsequent call of 'wait ()'.  If not done here,
+     we fetch the lower priority events in 'wait ()', report an UNAVAILABLE
+     status to GDB and then fetch the higher priority events in 'pause_all'.
+     In a live attach scenario, we don't receive a 'continue' resume request
+     and would miss the thread stopped event.  */
+  if (!non_stop)
+    fetch_events_all_devices_no_resumed ();
+
+  /* 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)
+{
+  gdb_assert (device.process != nullptr);
+
+  bool has_thread_to_resume = false;
+  device.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)
+{
+  /* Note that even if we stopped all, unavailable threads may still
+     report new events as we were not able to stop them.
+
+     We ignore those threads and the unavailable event they report.  */
+
+  size_t num_eventing = 0;
+  for_each_thread ([=, &num_eventing] (thread_info *tp)
+    {
+      if (!tp->id.matches (resume_ptid))
+	return;
+
+      if (!ze_has_priority_waitstatus (tp))
+	{
+	  (void) ze_move_waitstatus (tp);
+	  return;
+	}
+
+      ze_thread_info *zetp = ze_thread (tp);
+      gdb_assert (zetp != nullptr);
+
+      /* If the thread's stop event was being held, it is now the time
+	 to convert the state to 'stopped' to unleash the event.  */
+      if (zetp->exec_state == ZE_THREAD_STATE_HELD)
+	zetp->exec_state = ZE_THREAD_STATE_STOPPED;
+
+      /* 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.  */
+	  target_waitstatus waitstatus = ze_move_waitstatus (tp);
+	  if (waitstatus.kind () != TARGET_WAITKIND_STOPPED)
+	    warning (_("thread %s has waitstatus %s, expected 'STOPPED'."),
+		     tp->id.to_string ().c_str (),
+		     waitstatus.to_string ().c_str ());
+	  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);
+	  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 pick
+   a THREAD_UNAVAILABLE event for reporting as the last resort.
+
+   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.
+
+   In all-stop mode, we will ignore unavailable threads when
+   resuming the target.  So, unless we explicitly try to interact
+   with them, unavailable threads should be transparent to an
+   all-stop target.
+
+   In non-stop mode, we give more time for unavailable threads to
+   become available and report an event.  */
+
+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)
+    {
+      /* Only consider threads that were resumed.  */
+      ze_thread_resume_state_t state = ze_resume_state (tp);
+      if (state == ZE_THREAD_RESUME_NONE)
+	return false;
+
+      /* If this thread's event is being held, we do not pick it for
+	 reporting.  */
+      ze_thread_exec_state_t exec_state = ze_exec_state (tp);
+      if (exec_state == ZE_THREAD_STATE_HELD)
+	return false;
+
+      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)
+      {
+	/* Only consider threads that were resumed.  */
+	ze_thread_resume_state_t state = ze_resume_state (tp);
+	if (state == ZE_THREAD_RESUME_NONE)
+	  return false;
+
+	/* Threads with held events are not picked.  */
+	ze_thread_exec_state_t exec_state = ze_exec_state (tp);
+	if (exec_state == ZE_THREAD_STATE_HELD)
+	  return false;
+
+	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.
+
+	 For all-stop, we anyway want to stop all threads and drain events
+	 before reporting the stop to GDB.
+
+	 For non-stop, this will allow us to group stop events for multiple
+	 threads.  */
+      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);
+
+      /* Next, find a matching entity, whose event we'll report.
+
+	 We prioritize process events since they are typically a lot rarer and
+	 further have higher impact and should be handled before any thread
+	 events of that process.
+
+	 Process events are no stop events.  They leave threads running,
+	 even in all-stop mode.  */
+      process_info *process
+	= find_process ([ptid, this] (process_info *proc)
+	  {
+	    if (!ptid_t (proc->pid).matches (ptid))
+	      return false;
+
+	    process_info_private *zeproc = proc->priv;
+	    gdb_assert (zeproc != nullptr);
+
+	    return (zeproc->waitstatus.kind () != TARGET_WAITKIND_IGNORE);
+	  });
+
+      /* If we found a process event, it is our primary candidate.
+
+	 Process events with a low priority UNAVAILABLE waitstatus do not
+	 stop the target in all-stop, but some of its threads might have a
+	 pending waitstatus, which requires the stop.  If such THREAD is
+	 found, we prioritize it, clean the process waitstatus, and fall
+	 through to the thread reporting.  The process event will
+	 piggyback on it.
+
+	 We do not take any special care about fairness as we expect process
+	 events to be rather rare.  */
+      thread_info *thread = nullptr;
+      if (process != nullptr)
+	{
+	  process_info_private *zeproc = process->priv;
+	  gdb_assert (zeproc != nullptr);
+	  ptid_t process_ptid = ptid_t (process->pid);
+
+	  /* If we got an unavailable process event, try to find another
+	     eventing thread for this process.  */
+	  if (zeproc->waitstatus.kind () == TARGET_WAITKIND_UNAVAILABLE)
+	    thread = ze_find_eventing_thread (process_ptid);
+
+	  /* If not found, return the process and clean its waitstatus.  */
+	  if (thread == nullptr)
+	    {
+	      *status = zeproc->waitstatus;
+	      zeproc->waitstatus.set_ignore ();
+
+	      return process_ptid;
+	    }
+
+	  /* THREAD should always match the PTID: we got a process event,
+	     so PTID must be either minus_one or the process's ptid.  */
+	  gdb_assert (thread->id.matches (ptid));
+
+	  /* The process event will piggyback onto the THREAD event.
+	     However, we still need to clean the process status.  */
+	  zeproc->waitstatus.set_ignore ();
+	}
+
+      /* If we have previously found THREAD for the PROCESS, we use it.
+         Otherwise, proceed with searching for a thread event for PTID.  */
+      if (thread == nullptr)
+	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;
+	    }
+
+	  /* Resume any thread we didn't want stopped.  */
+	  if ((zetp->stop_reason == TARGET_STOPPED_BY_NO_REASON)
+	      && (zetp->waitstatus.kind () == TARGET_WAITKIND_STOPPED)
+	      && (zetp->waitstatus.sig () == GDB_SIGNAL_0))
+	    {
+	      dprintf ("silently resuming thread %s (%s)",
+		       thread->id.to_string ().c_str (),
+		       ze_thread_id_str (zetp->id).c_str ());
+
+	      /* Undo any previous holding of the event.  */
+	      zetp->exec_state = ZE_THREAD_STATE_STOPPED;
+	      zetp->waitstatus.set_ignore ();
+	      ze_set_resume_state (thread, resume_continue);
+
+	      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);
+
+	  /* 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 thread->id;
+	}
+
+      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);
+}
+
+/* Determine the thread id and device context for accessing ADDR_SPACE
+   from THREAD.  */
+static std::pair<ze_device_thread_t, ze_device_info *>
+ze_memory_access_context (thread_info *thread, unsigned int addr_space)
+{
+  /* With a stopped thread, we can access all address spaces, and we
+     should be able to determine the device for that thread.  */
+  if (ze_thread_stopped (thread))
+    return std::pair<ze_device_thread_t, ze_device_info *>
+      { ze_thread_id (thread), ze_thread_device (thread) };
+
+  /* Without a stopped thread, we may only access the default address
+     space and only in the context of thread ALL.  */
+  if (addr_space != ZET_DEBUG_MEMORY_SPACE_TYPE_DEFAULT)
+    error (_("need thread to access non-default address space."));
+
+  /* Try to determine the device using THREAD but fall back to the current
+     process' device, e.g. if THREAD is nullptr.  */
+  ze_device_info *device = ze_thread_device (thread);
+  if (device == nullptr)
+    {
+      process_info *process = current_process ();
+      device = ze_process_device (process);
+
+      if (device == nullptr)
+	error (_("cannot determine device for memory access."));
+    }
+
+  return std::pair<ze_device_thread_t, ze_device_info *>
+    { ze_thread_id_all (), device };
+}
+
+int
+ze_target::read_memory (thread_info *tp, 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;
+
+  std::pair<ze_device_thread_t, ze_device_info *> context
+    = ze_memory_access_context (tp, addr_space);
+  ze_device_thread_t thread = context.first;
+  ze_device_info *device = context.second;
+  gdb_assert (device != nullptr);
+
+  ze_result_t status = zetDebugReadMemory (device->session, thread, &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 (thread).c_str (), status);
+
+      return EIO;
+    }
+}
+
+int
+ze_target::read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
+{
+  return read_memory (current_thread, memaddr, myaddr, len);
+}
+
+int
+ze_target::write_memory (thread_info *tp, 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;
+
+  std::pair<ze_device_thread_t, ze_device_info *> context
+    = ze_memory_access_context (tp, addr_space);
+  ze_device_thread_t thread = context.first;
+  ze_device_info *device = context.second;
+  gdb_assert (device != nullptr);
+
+  dprintf ("writing %d bytes of memory to %s with %s",
+	   len, core_addr_to_string_nz (memaddr),
+	   ze_thread_id_str (thread).c_str ());
+
+  ze_result_t status = zetDebugWriteMemory (device->session, thread, &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 (thread).c_str (), status);
+
+      return EIO;
+    }
+}
+
+int
+ze_target::write_memory (CORE_ADDR memaddr, const unsigned char *myaddr,
+			 int len)
+{
+  return write_memory (current_thread, memaddr, myaddr, len);
+}
+
+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 ()
+{
+  if (current_process () == nullptr)
+    error (_("no current process."));
+
+  process_info *process = current_process ();
+  gdb_assert (process != nullptr);
+
+  process_info_private *priv = process->priv;
+  gdb_assert (priv != nullptr);
+
+  /* The only reason why we would not have a device is if we got detached.
+
+     There is nothing to interrupt in that case.  */
+  ze_device_info *device = priv->device;
+  if (device == nullptr)
+    return;
+
+  /* Interrupt is not a resume request.  */
+
+  ze_device_thread_t all = ze_thread_id_all ();
+  ze_interrupt (*device, all);
+}
+
+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;
+
+  /* Interrupting all threads on devices that have any resumed threads.
+
+     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;
+
+      if ((device->nresumed != 0) && (device->ninterrupts == 0))
+	ze_interrupt (*device, all);
+    }
+
+  /* Fetch events until no device has any resumed threads left.  */
+  fetch_events_all_devices_no_resumed ();
+
+  /* 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);
+
+      /* Clear the non-priority waitstatus so wait doesn't pick the thread
+	 to report an (unavailable) event we just fetched.  */
+      zetp->waitstatus.set_ignore ();
+
+      /* Ignore threads that aren't stopped, most likely because they are
+	 unavailable.
+
+	 Even though an unavailable thread may have responded to our
+	 interrupt, we do not mark it paused because we need to treat
+	 unavailable and stopped threads differently in unpause_all.  */
+      if (ze_thread_stopped (tp))
+	zetp->exec_state = ZE_THREAD_STATE_PAUSED;
+    });
+}
+
+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;
+
+  /* Resume threads that were marked by pause_all as well as unavailable
+     threads that were not requested to stop.
+
+     Pause_all leaves the latter marked unavailable.  We don't really
+     resume them as they were not actually stopped on the target, but we
+     need to update the thread state and some statistics.  */
+
+  /* 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_UNAVAILABLE:
+	  {
+	    /* Distinguish unavailable threads that we tried to interrupt
+	       in pause_all from those that GDB tried to interrupt with a
+	       stop resume request.  */
+	    ze_thread_resume_state_t resume_state = ze_resume_state (tp);
+	    if (resume_state != ZE_THREAD_RESUME_STOP)
+	      return;
+	  }
+
+	  [[fallthrough]];
+	case ZE_THREAD_STATE_STOPPED:
+	case ZE_THREAD_STATE_HELD:
+	  {
+	    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);
+
+	  ze_thread_exec_state_t state = zetp->exec_state;
+	  switch (state)
+	    {
+	    case ZE_THREAD_STATE_STOPPED:
+	    case ZE_THREAD_STATE_HELD:
+	    case ZE_THREAD_STATE_RUNNING:
+	    case ZE_THREAD_STATE_UNKNOWN:
+	      /* We already diagnosed unexpected states above.  */
+	      return;
+
+	    case ZE_THREAD_STATE_UNAVAILABLE:
+	      {
+		/* Don't touch threads that GDB wants stopped.  */
+		ze_thread_resume_state_t resume_state = ze_resume_state (tp);
+		if (resume_state == ZE_THREAD_RESUME_STOP)
+		  return;
+
+		/* We don't plan to resume but we still need to prepare TP
+		   for nresumed tracking and thread state management.  */
+		bool should_resume = ze_prepare_for_resuming (tp);
+		gdb_assert (!should_resume);
+	      }
+	      return;
+
+	    case ZE_THREAD_STATE_PAUSED:
+	      resume_single_thread (tp);
+	      return;
+	    }
+	});
+    }
+
+  /* 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 0000000000000000000000000000000000000000..2c48c49a914f884e4808f7da6b9e7884007925b5
--- /dev/null
+++ b/gdbserver/ze-low.h
@@ -0,0 +1,492 @@
+/* Target interface for Level-Zero based targets for gdbserver.
+   See https://github.com/oneapi-src/level-zero.git.
+
+   Copyright (C) 2020-2024 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_LEVEL_ZERO_LOW_H
+#define GDBSERVER_LEVEL_ZERO_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.  */
+  long 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 resumed threads.  The value is useful for deciding if
+     we can omit sending an actual interrupt request when we want all
+     threads to be stopped in all-stop mode.
+
+     The value can underflow because of unavailable threads becoming
+     available and generating stop events.  Therefore we pay care to
+     prevent underflowing.  */
+  unsigned long nresumed = 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 but we are holding its stop event until we
+     resume it.  */
+  ZE_THREAD_STATE_HELD,
+
+  /* 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 ().
+
+     Unavailable threads will not be resumed, so we keep those in state
+     unavailable and only clear their waitstatus to prevent them from
+     getting reported by wait ().  */
+  ZE_THREAD_STATE_PAUSED,
+
+  /* The thread is running.  We do not know whether it is still available
+     to us and we're able to stop it or whether it would eventually hit a
+     breakpoint.
+
+     When a thread completes executing a kernel it becomes idle and may
+     pick up other workloads, either in this context or in another
+     process' context.
+
+     In the former case, it would still be considered RUNNING from our
+     point of view, even though it started over again with a new set of
+     arguments.  In the latter case, it would be UNAVAILABLE.  */
+  ZE_THREAD_STATE_RUNNING,
+
+  /* The thread is currently not available to us.  It may be idle or it
+     may be executing work on behalf of a different process.
+
+     We cannot distinguish those cases.  We're not able to interact with
+     that thread.  It may become available again at any time, though.
+
+     From GDB's view, a thread may switch between RUNNING and UNAVAILABLE.
+     We will only know the difference when we try to stop it.  It's not
+     entirely clear whether we need to distinguish the two, at all.  */
+  ZE_THREAD_STATE_UNAVAILABLE
+};
+
+/* 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
+     or EXEC_STATE == ZE_THREAD_STATE_HELD.  */
+  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.  */
+
+extern std::string ze_thread_id_str (const ze_device_thread_t &thread);
+
+/* The state of a process.  */
+
+enum ze_process_state
+{
+  /* The process is visible to the user.  */
+  ZE_PROCESS_VISIBLE,
+
+  /* The process is hidden from the user.  */
+  ZE_PROCESS_HIDDEN
+};
+
+/* 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;
+
+  /* The state of this process.  */
+  ze_process_state state;
+
+  /* The waitstatus for this process's last event.
+
+     While stop events are reported on threads, module loads and unloads
+     as well as entry and exit are reports on the process itself.
+
+     Neither of these events implies that any of the process' threads
+     stopped or is even available.
+
+     TARGET_WAITKIND_IGNORE means that there is nothing to report.  */
+  target_waitstatus waitstatus {};
+
+  process_info_private (ze_device_info *dev, ze_process_state st)
+    : device (dev), state (st)
+    {}
+};
+
+/* 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::vector<char *> &argv) 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;
+
+  /* We model h/w threads - they do not exit.  */
+  bool thread_alive (ptid_t ptid) override { return true; }
+  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 ""; }
+
+  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;
+
+  /* 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);
+
+  /* Fetch events until no device has any resumed threads left.  */
+  void fetch_events_all_devices_no_resumed ();
+
+  /* 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;
+
+  /* TP stopped.  Find out why and return the stop reason.  Optionally
+     fill in SIGNAL.  */
+  virtual target_stop_reason get_stop_reason (thread_info *tp,
+					      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);
+
+  /* Write the memory in the context of thread TP.  */
+  int write_memory (thread_info *tp, CORE_ADDR memaddr,
+		    const unsigned char *myaddr, int len);
+};
+
+#endif /* GDBSERVER_LEVEL_ZERO_LOW_H */

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 39/47] testsuite, sycl: add SYCL support
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (37 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 38/47] gdbserver, ze, intelgt: introduce ze-low and intel-ze-low targets Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 40/47] testsuite, sycl: add test for backtracing inside a kernel Tankut Baris Aktemur
                   ` (8 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

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>
---
 gdb/testsuite/README                   |   9 +
 gdb/testsuite/boards/intel-offload.exp |  36 +++
 gdb/testsuite/gdb.sycl/break.exp       |  63 +++++
 gdb/testsuite/gdb.sycl/break2.exp      |  66 ++++++
 gdb/testsuite/gdb.sycl/single-task.cpp |  50 ++++
 gdb/testsuite/lib/gdb.exp              |  17 +-
 gdb/testsuite/lib/intelgt-utils.exp    |  43 ++++
 gdb/testsuite/lib/sycl-devices.cpp     | 107 +++++++++
 gdb/testsuite/lib/sycl-hello.cpp       |  43 ++++
 gdb/testsuite/lib/sycl-util.cpp        | 135 +++++++++++
 gdb/testsuite/lib/sycl.exp             | 410 +++++++++++++++++++++++++++++++++
 11 files changed, 978 insertions(+), 1 deletion(-)

diff --git a/gdb/testsuite/README b/gdb/testsuite/README
index 06664d723e7902a22fc9a0e63627e2780fae8d22..f18f205a05091109d8ec4585c12455cc6e4724d4 100644
--- a/gdb/testsuite/README
+++ b/gdb/testsuite/README
@@ -340,6 +340,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 100755
index 0000000000000000000000000000000000000000..5045a7718c9b068e0afc46de10c88de40aeba2fa
--- /dev/null
+++ b/gdb/testsuite/boards/intel-offload.exp
@@ -0,0 +1,36 @@
+# Copyright 2022-2024 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.exp b/gdb/testsuite/gdb.sycl/break.exp
new file mode 100644
index 0000000000000000000000000000000000000000..fe45a154bc015bf978a76b410758e7bb625c1f87
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/break.exp
@@ -0,0 +1,63 @@
+# Copyright 2019-2024 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 GDB's support for SYCL; in particular, inserting and hitting
+# breakpoints inside and outside a kernel.
+
+load_lib sycl.exp
+
+standard_testfile single-task.cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+    unsupported "target does not support SYCL"
+    return 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+    "${binfile}" $srcfile {sycl debug}]} {
+    return -1
+}
+
+foreach device $sycl_device_list {
+    sycl_with_intelgt_lock $device {
+	clean_restart "${binfile}"
+
+	if {![sycl_start $device]} {
+	    continue
+	}
+
+	# Set breakpoints inside and outside the kernel.
+	array set bp_locations_kernel {}
+	set num_kernel_locations 4
+
+	gdb_breakpoint "$srcfile:[gdb_get_line_number line-after-kernel]" \
+	    {message}
+
+	for {set i 1} {$i <= $num_kernel_locations} {incr i} {
+	    set bp_locations_kernel($i) [gdb_get_line_number "kernel-line-$i"]
+	    gdb_breakpoint "$srcfile:$bp_locations_kernel($i)" {message}
+	}
+
+	# Test that we actually hit the breakpoints.
+	for {set i 1} {$i <= $num_kernel_locations} {incr i} {
+	    gdb_continue_to_breakpoint "kernel line $i" \
+		".*$srcfile:$bp_locations_kernel($i).*"
+	}
+
+	gdb_continue_to_breakpoint "line after kernel" \
+	    ".*$srcfile:[gdb_get_line_number line-after-kernel].*"
+    }
+}
diff --git a/gdb/testsuite/gdb.sycl/break2.exp b/gdb/testsuite/gdb.sycl/break2.exp
new file mode 100644
index 0000000000000000000000000000000000000000..33ec33addf739f476615303c7574f67eae109d87
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/break2.exp
@@ -0,0 +1,66 @@
+# Copyright 2019-2024 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 GDB's support for SYCL; in particular, inserting and hitting
+# breakpoints inside and outside a kernel.  This test is similar to
+# break.exp except that not all the breakpoints are defined at the
+# beginning.  A couple in- and out-kernel breakpoints are defined when
+# we are inside the kernel.
+
+load_lib sycl.exp
+
+standard_testfile single-task.cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+    unsupported "target does not support SYCL"
+    return 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+    "${binfile}" $srcfile {sycl debug}]} {
+    return -1
+}
+
+foreach device $sycl_device_list {
+    sycl_with_intelgt_lock $device {
+	clean_restart "${binfile}"
+
+	if {![sycl_start $device]} {
+	    continue
+	}
+
+	# Set breakpoints inside and outside the kernel.
+	set after_kernel [gdb_get_line_number "line-after-kernel"]
+	set inside_kernel_1 [gdb_get_line_number "kernel-line-1"]
+	set inside_kernel_2 [gdb_get_line_number "kernel-line-2"]
+	set inside_kernel_3 [gdb_get_line_number "kernel-line-3"]
+	set inside_kernel_4 [gdb_get_line_number "kernel-line-4"]
+
+	gdb_breakpoint $inside_kernel_2 {message}
+	gdb_continue_to_breakpoint "kernel line 2" \
+	    ".*$srcfile:$inside_kernel_2.*"
+
+	# Now we are inside the kernel and defining a kernel breakpoint.
+	gdb_breakpoint $inside_kernel_4 {message}
+	gdb_continue_to_breakpoint "kernel line 4" \
+	    ".*$srcfile:$inside_kernel_4.*"
+
+	# We are still inside the kernel and defining a host breakpoint.
+	gdb_breakpoint $after_kernel {message}
+	gdb_continue_to_breakpoint "line after kernel" \
+	    ".*$srcfile:$after_kernel.*"
+    }
+}
diff --git a/gdb/testsuite/gdb.sycl/single-task.cpp b/gdb/testsuite/gdb.sycl/single-task.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..dc6105c03174da7ba7af98dce59ff1b5d95831c8
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/single-task.cpp
@@ -0,0 +1,50 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2019-2024 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 "../lib/sycl-util.cpp"
+
+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)  /* line-before-kernel */
+      {
+	auto numbers = buf.get_access<sycl::access::mode::read_write> (cgh);
+
+	cgh.single_task<class simple_kernel> ([=] ()
+	  {
+	    int ten = numbers[1] + 2;  /* kernel-line-1 */
+	    int four = numbers[2] - 5; /* kernel-line-2 */
+	    int fourteen = ten + four; /* kernel-line-3 */
+	    numbers[0] = fourteen * 3; /* kernel-line-4 */
+	  });
+      });
+  }
+
+#ifndef OMIT_REPORT
+  std::cout << "Result is " << data[0] << std::endl;  /* line-after-kernel */
+#endif
+
+  return data[0] == 42 ? 0 : 1; /* return-stmt */
+}
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 7ee2043f0f88602f64943e5083be2566b1f33692..d7179db4af47b2e1f01d73875a4294329ab16112 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -6526,6 +6526,21 @@ proc gdb_compile_openmp {source dest type options} {
     return [gdb_compile $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
 
@@ -8729,7 +8744,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 0000000000000000000000000000000000000000..6e99c0fe78c74dd12b3c6f97eff30f86bbe97d3f
--- /dev/null
+++ b/gdb/testsuite/lib/intelgt-utils.exp
@@ -0,0 +1,43 @@
+# Copyright 2020-2024 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 0000000000000000000000000000000000000000..ac1ca67faaf31934576a2b8c0932ca9165f8df04
--- /dev/null
+++ b/gdb/testsuite/lib/sycl-devices.cpp
@@ -0,0 +1,107 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2022-2024 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 dev_name
+	= device.get_info<sycl::info::device::name> ();
+      const std::string version
+	= device.get_info<sycl::info::device::driver_version> ();
+      const std::string backend_name
+	= get_backend_name (device.get_backend ());
+
+      const std::string type
+	= get_device_type (device.get_info<sycl::info::device::device_type> ());
+
+      if (backend_name == "")
+	continue;
+
+      std::string dev_key {dev_name + ";" + backend_name + ";" + version
+			   + ";" + type};
+      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 0000000000000000000000000000000000000000..8de4892ce31d2111e2c11f15b72226c80724eac5
--- /dev/null
+++ b/gdb/testsuite/lib/sycl-hello.cpp
@@ -0,0 +1,43 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2019-2024 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 numbers[8];
+
+int
+main (int argc, char *argv[])
+{
+  sycl::queue deviceQueue {get_sycl_queue (argc, argv)};
+  sycl::range<1> length {8};
+
+  { /* Extra scope enforces waiting on the kernel.  */
+    sycl::buffer<int, 1> buf {numbers, length};
+    deviceQueue.submit ([&] (sycl::handler& cgh)
+      {
+	auto accessor = buf.get_access<sycl::access::mode::read_write> (cgh);
+
+	cgh.parallel_for<class SyclHello> (length, [=] (sycl::id<1> wiID)
+	  {
+	    accessor[wiID] = wiID[0] + 1; /* inside-kernel */
+	  });
+      });
+  }
+
+  return 0;
+}
diff --git a/gdb/testsuite/lib/sycl-util.cpp b/gdb/testsuite/lib/sycl-util.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..99e6981d8b2df84cfc4f0540a1111fdadd095416
--- /dev/null
+++ b/gdb/testsuite/lib/sycl-util.cpp
@@ -0,0 +1,135 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2019-2024 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 0000000000000000000000000000000000000000..c7e0d527bd3c1e6b54523ec5b4ecee4786094e02
--- /dev/null
+++ b/gdb/testsuite/lib/sycl.exp
@@ -0,0 +1,410 @@
+# Copyright 2019-2024 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
+
+# Define global variables for the driver version and platform.
+
+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.
+# Input arg DEVICE contains ";" separated following information:
+# Device name;Backend Type;Backend Platform version.
+# See an example in init_sycl_devices_list.
+
+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 {[lindex $args_list 0] eq $type}]
+    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 1
+    }
+    verbose -log "\nSYCL: Compilation succeeded"
+
+    foreach device $sycl_device_list {
+	if ![is_sycl_device_filtered $device] {
+	    continue
+	}
+
+	clean_restart "${exe}"
+
+	if {![sycl_start $device]} {
+	    verbose "SYCL: Support not detected for ${device}" 0
+	    gdb_exit
+	    continue
+	}
+
+	set inside_kernel [gdb_get_line_number "inside-kernel" $src]
+	gdb_breakpoint "sycl-hello.cpp:$inside_kernel"
+
+	set result 1
+	sycl_with_intelgt_lock $device {
+	    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
+	}
+
+	gdb_exit
+    }
+
+    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 "zeContextCreate" {*}"allow-pending temporary"
+	set hook_bp_hit [gdb_continue_to_breakpoint "hook bp" \
+			     "zeContextCreate\[^\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 --attach - $inf_pid"
+	    gdb_test "target remote | $cmd" \
+		"Remote debugging.*" "connect the remote target"
+	    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 Type;Backend Platform version.
+# e.g.
+# Intel(R) ..;ext_oneapi_level_zero;1.3.24347
+
+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 {[prepare_for_testing "failed to compile $src" \
+	     $exe $src {sycl debug}]} {
+	verbose "SYCL: Compilation failed" 0
+	return $sycl_device_list
+    }
+    verbose -log "\nSYCL: Compilation succeeded"
+
+    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 Type;Backend Platform version.
+# See an example in init_sycl_devices_list.
+
+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 3]
+
+    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.
+
+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 Type;Backend Platform version;Device Type;count.
+# See an example in init_sycl_devices_list.
+
+proc sycl_get_device_args {device} {
+    global hex
+
+    set device_info [split "$device" ";"]
+    set sycl_driver_platform [lindex $device_info 1]
+    set sycl_driver_version [lindex $device_info 2]
+    set device_type [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 $sycl_driver_platform
+    lappend args_list $sycl_driver_version
+    return $args_list
+}
+
+# Gets the prefix string required for the SYCL tests.
+# DEVICE contains ";" separated following information:
+# Device name;Backend Type;Backend Platform version;Device type.
+# e.g.
+# Intel(R) ...;ext_oneapi_level_zero;1.3.24347;gpu
+# 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.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 40/47] testsuite, sycl: add test for backtracing inside a kernel
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (38 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 39/47] testsuite, sycl: add SYCL support Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 41/47] testsuite, sycl: add test for 'info locals' and 'info args' Tankut Baris Aktemur
                   ` (7 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

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 | 179 ++++++++++++++++++++++++++++++++++
 2 files changed, 271 insertions(+)

diff --git a/gdb/testsuite/gdb.sycl/call-stack.cpp b/gdb/testsuite/gdb.sycl/call-stack.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7e6a44f06c78b77bd14e3fc4c1e34fef302856d2
--- /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-2024 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 "../lib/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 0000000000000000000000000000000000000000..aeca934903339bc2c44d2d811a9b3626ab86166b
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/call-stack.exp
@@ -0,0 +1,179 @@
+# Copyright 2019-2024 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 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+    "${binfile}" $srcfile {sycl debug}]} {
+    return -1
+}
+
+# Return the current line number.
+proc get_current_line {} {
+    global decimal gdb_prompt
+    gdb_test_multiple "info line" "get current line" {
+	-re "Line ($decimal).*$gdb_prompt $" {
+	    pass $gdb_test_name
+	    return $expect_out(1,string)
+	}
+	-re "$gdb_prompt $" {
+	    fail $gdb_test_name
+	    return 0
+	}
+    }
+}
+
+proc test_call_stack {device} {
+    global srcfile valnum_re decimal inferior_exited_re gdb_prompt
+
+    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"
+
+    # Test breaking on function names inside the kernel.
+    gdb_breakpoint "first"
+
+    gdb_test "continue" ".*$srcfile:$first_loc.*"
+
+    # Set breakpoint inside the kernel.
+    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...)".
+    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 "${binfile}"
+
+	if {![sycl_start $device]} {
+	    continue
+	}
+
+	test_call_stack "$device"
+    }
+}

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 41/47] testsuite, sycl: add test for 'info locals' and 'info args'
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (39 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 40/47] testsuite, sycl: add test for backtracing inside a kernel Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 15:59 ` [PATCH v2 42/47] testsuite, sycl: add tests for stepping and accessing data elements Tankut Baris Aktemur
                   ` (6 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

Test that local variables and arguments inside a SYCL kernel can be
displayed correctly.
---
 gdb/testsuite/gdb.sycl/info-locals-and-args.exp | 78 +++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

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 0000000000000000000000000000000000000000..289d3656afdddc9921c9c38487c5c7cf92996993
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/info-locals-and-args.exp
@@ -0,0 +1,78 @@
+# Copyright 2019-2024 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 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+    "${binfile}" $srcfile {sycl debug}]} {
+    return -1
+}
+
+foreach device $sycl_device_list {
+    sycl_with_intelgt_lock $device {
+	clean_restart "${binfile}"
+
+	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.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 42/47] testsuite, sycl: add tests for stepping and accessing data elements
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (40 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 41/47] testsuite, sycl: add test for 'info locals' and 'info args' Tankut Baris Aktemur
@ 2024-12-13 15:59 ` Tankut Baris Aktemur
  2024-12-13 16:00 ` [PATCH v2 43/47] testsuite, sycl: add test for 1-D and 2-D parallel_for kernels Tankut Baris Aktemur
                   ` (5 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 15:59 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

Add SYCL test cases for
- stepping inside the kernel
- reading data elements and locals
- modifying data elements and locals
---
 gdb/testsuite/gdb.sycl/step-into-function.exp | 47 ++++++++++++++++++++++++
 gdb/testsuite/gdb.sycl/step.exp               | 51 +++++++++++++++++++++++++++
 2 files changed, 98 insertions(+)

diff --git a/gdb/testsuite/gdb.sycl/step-into-function.exp b/gdb/testsuite/gdb.sycl/step-into-function.exp
new file mode 100644
index 0000000000000000000000000000000000000000..06d0046f46f1be92a997343be6d718c9d702caeb
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/step-into-function.exp
@@ -0,0 +1,47 @@
+# Copyright 2019-2024 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 stepping into a kernel function.
+
+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 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+    "${binfile}" $srcfile {sycl debug}]} {
+    return -1
+}
+
+foreach device $sycl_device_list {
+    sycl_with_intelgt_lock $device {
+	clean_restart "${binfile}"
+
+	if {![sycl_start $device]} {
+	    continue
+	}
+
+	set inside [gdb_get_line_number "ordinary-outer-loc"]
+	gdb_breakpoint $inside
+	gdb_continue_to_breakpoint "inside" ".*$srcfile:$inside.*"
+
+	gdb_test "step" "first .* at .*" "step into function"
+    }
+}
diff --git a/gdb/testsuite/gdb.sycl/step.exp b/gdb/testsuite/gdb.sycl/step.exp
new file mode 100644
index 0000000000000000000000000000000000000000..b16679f8704869cf7c0df061c40dde66b08b8b81
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/step.exp
@@ -0,0 +1,51 @@
+# Copyright 2019-2024 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; in particular, single-stepping the
+# source and printing values of local vars and data elements.
+
+load_lib sycl.exp
+
+standard_testfile single-task.cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+    unsupported "target does not support SYCL"
+    return 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+    "${binfile}" $srcfile {sycl debug}]} {
+    return -1
+}
+
+foreach device $sycl_device_list {
+    sycl_with_intelgt_lock $device {
+	clean_restart "${binfile}"
+
+	if {![sycl_start $device]} {
+	    continue
+	}
+
+	# Break at the first line of the kernel, then make steps.
+	set first_line [gdb_get_line_number "kernel-line-1"]
+	gdb_breakpoint $first_line
+	gdb_continue_to_breakpoint "first line" ".*$srcfile:$first_line.*"
+
+	gdb_test "next" "int four = .*" "next 1"
+	gdb_test "next" "int fourteen = .*" "next 2"
+	gdb_test "next" "numbers.0. = .*" "next 3"
+    }
+}

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 43/47] testsuite, sycl: add test for 1-D and 2-D parallel_for kernels
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (41 preceding siblings ...)
  2024-12-13 15:59 ` [PATCH v2 42/47] testsuite, sycl: add tests for stepping and accessing data elements Tankut Baris Aktemur
@ 2024-12-13 16:00 ` Tankut Baris Aktemur
  2024-12-13 16:00 ` [PATCH v2 44/47] testsuite, sycl: add test for scheduler-locking Tankut Baris Aktemur
                   ` (4 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 16:00 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

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   | 55 +++++++++++++++++++++
 gdb/testsuite/gdb.sycl/parallel-for-2D.cpp   | 73 ++++++++++++++++++++++++++++
 gdb/testsuite/gdb.sycl/parallel-for-2D.exp   | 55 +++++++++++++++++++++
 gdb/testsuite/gdb.sycl/step-parallel-for.exp | 63 ++++++++++++++++++++++++
 5 files changed, 318 insertions(+)

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 0000000000000000000000000000000000000000..bbcff7253b61c5ca53c3bb23a27500f46676a43f
--- /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-2024 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 "../lib/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> bufferIn {&in[0], dataRange};
+    sycl::buffer<int, 1> bufferOut {&out[0], dataRange};
+
+    deviceQueue.submit ([&] (sycl::handler& cgh) /* line-before-kernel */
+      {
+	auto accessorIn = bufferIn.get_access<sycl::access::mode::read> (cgh);
+	auto accessorOut
+	  = bufferOut.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 0000000000000000000000000000000000000000..53e2d902b0fc8e83a013c899622f217638926ab6
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/parallel-for-1D.exp
@@ -0,0 +1,55 @@
+# Copyright 2019-2024 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 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+    "${binfile}" $srcfile {sycl debug}]} {
+    return -1
+}
+
+foreach device $sycl_device_list {
+    sycl_with_intelgt_lock $device {
+	clean_restart "${binfile}"
+
+	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 0000000000000000000000000000000000000000..0607f8ffd2559c989236dbc0a9dc5220febfd46f
--- /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-2024 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 "../lib/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> bufferIn {&in[0][0], dataRangeIn};
+    sycl::buffer<int, 2> bufferOut {&out[0][0], dataRangeOut};
+
+    deviceQueue.submit ([&] (sycl::handler& cgh)
+      {
+	auto accessorIn = bufferIn.get_access<sycl::access::mode::read> (cgh);
+	auto accessorOut
+	  = bufferOut.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 0000000000000000000000000000000000000000..580e65cee15c1722d7760ffac14b969b7219999d
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/parallel-for-2D.exp
@@ -0,0 +1,55 @@
+# Copyright 2019-2024 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 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+    "${binfile}" $srcfile {sycl debug}]} {
+    return -1
+}
+
+foreach device $sycl_device_list {
+    sycl_with_intelgt_lock $device {
+	clean_restart "${binfile}"
+
+	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/step-parallel-for.exp b/gdb/testsuite/gdb.sycl/step-parallel-for.exp
new file mode 100644
index 0000000000000000000000000000000000000000..0504843a36088dc70ec15f526fb1006717da28ea
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/step-parallel-for.exp
@@ -0,0 +1,63 @@
+# Copyright 2019-2024 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 stepping inside a parallel_for 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 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+    "${binfile}" $srcfile {sycl debug}]} {
+    return -1
+}
+
+foreach device $sycl_device_list {
+    sycl_with_intelgt_lock $device {
+	clean_restart "${binfile}"
+
+	if {![sycl_start $device]} {
+	    continue
+	}
+
+	set first_line [gdb_get_line_number "kernel-first-line"]
+	gdb_breakpoint $first_line
+
+	# Check that we can step inside the kernel. 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.
+	#
+	# Lock the scheduler for stepping to avoid inference.
+
+	gdb_test_no_output "set scheduler-locking step"
+
+	for {set i 1} {$i <= 5} {incr i} { with_test_prefix "trip $i" {
+	    gdb_continue_to_breakpoint "hit the first line" \
+		".*$srcfile:$first_line.*"
+
+	    gdb_test "next" "int in_elem = .*" "next 1"
+	    gdb_test "next" "int in_elem2 = .*" "next 2"
+	    gdb_test "next" "accessorOut.wiID. = in_elem.*" "next 3"
+	}}
+    }
+}

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 44/47] testsuite, sycl: add test for scheduler-locking
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (42 preceding siblings ...)
  2024-12-13 16:00 ` [PATCH v2 43/47] testsuite, sycl: add test for 1-D and 2-D parallel_for kernels Tankut Baris Aktemur
@ 2024-12-13 16:00 ` Tankut Baris Aktemur
  2024-12-13 16:00 ` [PATCH v2 45/47] testsuite, arch, intelgt: add a disassembly test Tankut Baris Aktemur
                   ` (3 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 16:00 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

Add a new test to check that scheduler-locking when inside a SYCL
kernel works fine.
---
 gdb/testsuite/gdb.sycl/scheduler-locking.exp | 67 ++++++++++++++++++++++++++++
 1 file changed, 67 insertions(+)

diff --git a/gdb/testsuite/gdb.sycl/scheduler-locking.exp b/gdb/testsuite/gdb.sycl/scheduler-locking.exp
new file mode 100644
index 0000000000000000000000000000000000000000..a5e9d23c03d2095117d94a20ce278b6e77255b56
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/scheduler-locking.exp
@@ -0,0 +1,67 @@
+# Copyright 2020-2024 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 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+    "${binfile}" $srcfile {sycl debug}]} {
+    return -1
+}
+
+foreach device $sycl_device_list {
+    sycl_with_intelgt_lock $device {
+	clean_restart "${binfile}"
+
+	if {![sycl_start $device]} {
+	    continue
+	}
+
+	set first_line [gdb_get_line_number "kernel-first-line"]
+	set second_line [expr $first_line + 1]
+	set third_line [expr $first_line + 2]
+	set fourth_line [expr $first_line + 3]
+	gdb_breakpoint $first_line
+	gdb_breakpoint $second_line
+	gdb_breakpoint $third_line
+	gdb_breakpoint $fourth_line
+
+	gdb_continue_to_breakpoint "hit the first line" \
+	    ".*$srcfile:$first_line.*"
+
+	gdb_test_no_output "set scheduler-locking on"
+
+	# Resuming should make only the current thread move.
+	set current_thread [get_current_thread "line $first_line"]
+	gdb_test "continue" \
+	    "Thread $current_thread \[^\r\n\]+$srcfile:$second_line.*" \
+	    "hit the second line"
+	gdb_test "continue" \
+	    "Thread $current_thread \[^\r\n\]+$srcfile:$third_line.*" \
+	    "hit the third line"
+	gdb_test "continue" \
+	    "Thread $current_thread \[^\r\n\]+$srcfile:$fourth_line.*" \
+	    "hit the fourth line"
+    }
+}

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 45/47] testsuite, arch, intelgt: add a disassembly test
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (43 preceding siblings ...)
  2024-12-13 16:00 ` [PATCH v2 44/47] testsuite, sycl: add test for scheduler-locking Tankut Baris Aktemur
@ 2024-12-13 16:00 ` Tankut Baris Aktemur
  2024-12-13 16:00 ` [PATCH v2 46/47] testsuite, arch, intelgt: add intelgt-program-bp.exp Tankut Baris Aktemur
                   ` (2 subsequent siblings)
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 16:00 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

From: Albertano Caruso <albertano.caruso@intel.com>

Test the disassemble command on the Intel GT target.
---
 gdb/testsuite/gdb.arch/intelgt-disassemble.exp | 82 ++++++++++++++++++++++++++
 gdb/testsuite/gdb.arch/sycl-simple.cpp         | 42 +++++++++++++
 2 files changed, 124 insertions(+)

diff --git a/gdb/testsuite/gdb.arch/intelgt-disassemble.exp b/gdb/testsuite/gdb.arch/intelgt-disassemble.exp
new file mode 100644
index 0000000000000000000000000000000000000000..8269d2ba2ccf3166b7d03a4f47724b7e0cc8b3bc
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/intelgt-disassemble.exp
@@ -0,0 +1,82 @@
+# Copyright 2019-2024 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-simple.cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+    unsupported "target does not support SYCL"
+    return 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+    "${binfile}" $srcfile {sycl debug}]} {
+    return -1
+}
+
+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 "${binfile}"
+
+	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+64" \
+		[multi_line "Dump of assembler code from $hex to $hex:" \
+		     "=> $hex .*" \
+		     "End of assembler dump."] \
+		"disassemble"
+	} else {
+	    unsupported "disassemble"
+
+	    gdb_test "disassemble \$pc, \$pc+64" \
+		[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-simple.cpp b/gdb/testsuite/gdb.arch/sycl-simple.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a51fd1ac3fda687879fddf909f246059a54c796f
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/sycl-simple.cpp
@@ -0,0 +1,42 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2019-2024 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 "../lib/sycl-util.cpp"
+
+int
+main (int argc, char *argv[])
+{
+  int data = 42;
+
+  { /* Extra scope enforces waiting on the kernel.  */
+    sycl::queue deviceQueue {get_sycl_queue (argc, argv)};
+    sycl::buffer<int, 1> buf {&data, sycl::range<1> {1}};
+
+    deviceQueue.submit ([&] (sycl::handler& cgh)
+      {
+	auto accessor = buf.get_access<sycl::access::mode::write> (cgh);
+
+	cgh.single_task<class simple_kernel> ([=] ()
+	  {
+	    accessor[0] = 99; /* inside-kernel */
+	  });
+      });
+  }
+
+  return 0;
+}

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 46/47] testsuite, arch, intelgt: add intelgt-program-bp.exp
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (44 preceding siblings ...)
  2024-12-13 16:00 ` [PATCH v2 45/47] testsuite, arch, intelgt: add a disassembly test Tankut Baris Aktemur
@ 2024-12-13 16:00 ` Tankut Baris Aktemur
  2024-12-13 16:00 ` [PATCH v2 47/47] testsuite, sycl: test canceling a stepping flow Tankut Baris Aktemur
  2025-02-07 10:18 ` [PATCH v2 00/47] A new target to debug Intel GPUs Aktemur, Tankut Baris
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 16:00 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

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(+)

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 0000000000000000000000000000000000000000..0b864c9425ba772ae1f5cb1f7cee7bf848e14b43
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/intelgt-program-bp.exp
@@ -0,0 +1,83 @@
+# Copyright 2020-2024 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-simple.cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+    unsupported "target does not support SYCL"
+    return 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+    "${binfile}" $srcfile {sycl debug}]} {
+    return -1
+}
+
+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 "${binfile}"
+
+	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.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* [PATCH v2 47/47] testsuite, sycl: test canceling a stepping flow
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (45 preceding siblings ...)
  2024-12-13 16:00 ` [PATCH v2 46/47] testsuite, arch, intelgt: add intelgt-program-bp.exp Tankut Baris Aktemur
@ 2024-12-13 16:00 ` Tankut Baris Aktemur
  2025-02-07 10:18 ` [PATCH v2 00/47] A new target to debug Intel GPUs Aktemur, Tankut Baris
  47 siblings, 0 replies; 87+ messages in thread
From: Tankut Baris Aktemur @ 2024-12-13 16:00 UTC (permalink / raw)
  To: gdb-patches, Markus Metzger

If there exists a pending event for stepping in a range, this means
the stepping flow was broken because of another event, such as another
thread hitting a breakpoint.  The stepping flow in this case is
canceled.  Test it.
---
 gdb/testsuite/gdb.sycl/step-canceled.exp | 86 ++++++++++++++++++++++++++++++++
 1 file changed, 86 insertions(+)

diff --git a/gdb/testsuite/gdb.sycl/step-canceled.exp b/gdb/testsuite/gdb.sycl/step-canceled.exp
new file mode 100644
index 0000000000000000000000000000000000000000..0b03dd11495e297fca0684b5c0334e138a05d91b
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/step-canceled.exp
@@ -0,0 +1,86 @@
+# Copyright 2023-2024 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 target's handling of stepping.
+#
+# In this scenario we start stepping for a thread and resume the
+# others.  There is a breakpoint close to the current PC.  We expect
+# other threads to hit that BP before the current thread finishes
+# stepping its range.  Hence, the step flow would be broken and
+# canceled.  We should be able to resume the thread without problems.
+
+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 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+    "${binfile}" $srcfile {sycl debug}]} {
+    return -1
+}
+
+foreach device $sycl_device_list {
+    sycl_with_intelgt_lock $device {
+	clean_restart "${binfile}"
+
+	if {![sycl_start $device]} {
+	    continue
+	}
+
+	set first_line [gdb_get_line_number "kernel-first-line"]
+	set next_line [expr $first_line + 1]
+	gdb_breakpoint $first_line {temporary}
+
+	# Explicitly control the scheduler-locking setting.
+	gdb_test_no_output "set scheduler-locking off"
+
+	gdb_continue_to_breakpoint "hit the first line" \
+	    ".*$srcfile:$first_line.*"
+	gdb_breakpoint $next_line
+
+	set first_thread [get_current_thread "first bp line"]
+
+	# Now only the second BP is there.  The "step" command will
+	# start a stepping flow for the first thread whereas it will
+	# resume the others.  We expect the others to hit the BP,
+	# because it is very close to our current PC, but of course
+	# there is a slight chance that this will not happen and the
+	# first thread will win the race.
+	gdb_test "step" "$srcfile:$next_line.*" "start stepping"
+	set second_thread [get_current_thread "second bp line"]
+
+	if {$first_thread eq $second_thread} {
+	    # There was really a very very low chance of this happening.
+	    untested "test condition could not be satisfied"
+	    continue
+	}
+
+	# Resume the first thread only.  It should hit the second BP
+	# normally.
+	gdb_test_no_output "set scheduler-locking on"
+	gdb_test "thread $first_thread" ".*" "switch to the first thread"
+	gdb_continue_to_breakpoint "resume the thread" \
+	    ".*$srcfile:$next_line.*"
+
+	set curr_thread [get_current_thread "second bp line again"]
+	gdb_assert {"$curr_thread" eq "$first_thread"} \
+	    "sanity-check the current thread"
+    }
+}

-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [PATCH v2 11/47] gdb, gdbserver, rsp, ze: acknowledge libraries
  2024-12-13 15:59 ` [PATCH v2 11/47] gdb, gdbserver, rsp, ze: acknowledge libraries Tankut Baris Aktemur
@ 2024-12-13 16:43   ` Eli Zaretskii
  2025-07-16  4:20   ` Thiago Jung Bauermann
  1 sibling, 0 replies; 87+ messages in thread
From: Eli Zaretskii @ 2024-12-13 16:43 UTC (permalink / raw)
  To: Tankut Baris Aktemur; +Cc: gdb-patches, markus.t.metzger

> From: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
> Date: Fri, 13 Dec 2024 16:59:28 +0100
> 
>  gdb/NEWS                      |   7 +++
>  gdb/doc/gdb.texinfo           |  75 +++++++++++++++++++++++++----
>  gdb/features/library-list.dtd |  10 ++--
>  gdb/remote.c                  |  81 ++++++++++++++++++++++++++++++++
>  gdb/solib-target.c            |  59 ++++++++++++++++++++++-
>  gdb/solib.c                   |  24 +++++++++-
>  gdb/solist.h                  |   7 +++
>  gdb/target-delegates-gen.c    |  50 ++++++++++++++++++++
>  gdb/target.c                  |  16 +++++++
>  gdb/target.h                  |  21 +++++++++
>  gdbserver/dll.cc              | 100 ++++++++++++++++++++++++++++++++++++---
>  gdbserver/dll.h               |  25 +++++++---
>  gdbserver/server.cc           | 107 +++++++++++++++++++++++++++++++++++++++---
>  gdbserver/server.h            |   4 ++
>  gdbserver/target.cc           |  14 ++++++
>  gdbserver/target.h            |  20 ++++++++
>  16 files changed, 585 insertions(+), 35 deletions(-)

Thanks, the documentation parts are okay.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [PATCH v2 07/47] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description
  2024-12-13 15:59 ` [PATCH v2 07/47] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description Tankut Baris Aktemur
@ 2024-12-13 16:45   ` Eli Zaretskii
  2025-07-08  4:04   ` Thiago Jung Bauermann
  1 sibling, 0 replies; 87+ messages in thread
From: Eli Zaretskii @ 2024-12-13 16:45 UTC (permalink / raw)
  To: Tankut Baris Aktemur; +Cc: gdb-patches, markus.t.metzger

> From: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
> Date: Fri, 13 Dec 2024 16:59:24 +0100
> 
>  gdb/NEWS                    |  8 ++++
>  gdb/doc/gdb.texinfo         | 25 +++++++++++++
>  gdb/features/gdb-target.dtd | 19 +++++++++-
>  gdb/target-descriptions.c   | 19 ++++++++++
>  gdb/xml-tdesc.c             | 76 ++++++++++++++++++++++++++++++++++++++
>  gdbserver/tdesc.cc          | 16 ++++++++
>  gdbserver/tdesc.h           |  3 ++
>  gdbsupport/tdesc.cc         | 40 ++++++++++++++++++++
>  gdbsupport/tdesc.h          | 90 +++++++++++++++++++++++++++++++++++++++++++++
>  9 files changed, 295 insertions(+), 1 deletion(-)

Thanks.

> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -174,6 +174,14 @@ vFile:stat
>    vFile:fstat but takes a filename rather than an open file
>    descriptor.
>  
> +* 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.
                              ^^^
Extra whitespace.

> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo

This part is okay.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [PATCH v2 03/47] ld: add intelgt as a target configuration
  2024-12-13 15:59 ` [PATCH v2 03/47] ld: add intelgt as a target configuration Tankut Baris Aktemur
@ 2024-12-16  7:43   ` Jan Beulich
  0 siblings, 0 replies; 87+ messages in thread
From: Jan Beulich @ 2024-12-16  7:43 UTC (permalink / raw)
  To: Tankut Baris Aktemur; +Cc: gdb-patches, binutils, Markus Metzger

On 13.12.2024 16:59, Tankut Baris Aktemur wrote:
> Add 'intelgt-*-elf' as a target configuration for ld.
> 
> To: <binutils@sourceware.org>
> ---
>  ld/configure.tgt | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/ld/configure.tgt b/ld/configure.tgt
> index b5f272e5ba28eed7ddaf0039cd5f916199c597e7..e4394828ad2387f147ba79155f1623edf691f00b 100644
> --- a/ld/configure.tgt
> +++ b/ld/configure.tgt
> @@ -468,6 +468,8 @@ ia64-*-*vms*)		targ_emul=elf64_ia64_vms
>  			;;
>  ia64-*-aix*)		targ_emul=elf64_aix
>  			;;
> +intelgt-*-elf)		targ_emul=elf_x86_64

If this is really intended, I'm pretty sure something needs saying about
this in the description. As written it looks like a copy-and-paste error.

Jan

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [PATCH v2 04/47] opcodes: add intelgt as a configuration
  2024-12-13 15:59 ` [PATCH v2 04/47] opcodes: add intelgt as a configuration Tankut Baris Aktemur
@ 2024-12-16  7:44   ` Jan Beulich
  2024-12-17 18:47     ` Aktemur, Tankut Baris
  0 siblings, 1 reply; 87+ messages in thread
From: Jan Beulich @ 2024-12-16  7:44 UTC (permalink / raw)
  To: Tankut Baris Aktemur; +Cc: gdb-patches, Markus Metzger, binutils

On 13.12.2024 16:59, Tankut Baris Aktemur wrote:
> Intel GT does not use the opcodes lib for disassembling instructions.

How else is disassembly going to work?

Jan

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [PATCH v2 01/47] gdb, intelgt: add intelgt as a basic machine
  2024-12-13 15:59 ` [PATCH v2 01/47] gdb, intelgt: add intelgt as a basic machine Tankut Baris Aktemur
@ 2024-12-16  7:53   ` Jan Beulich
  2024-12-17 18:48     ` Aktemur, Tankut Baris
  0 siblings, 1 reply; 87+ messages in thread
From: Jan Beulich @ 2024-12-16  7:53 UTC (permalink / raw)
  To: Tankut Baris Aktemur
  Cc: gdb-patches, Markus Metzger, config-patches, binutils

On 13.12.2024 16:59, Tankut Baris Aktemur wrote:
> Add 'intelgt' as a basic machine to config.sub.
> 
> To: <config-patches@gnu.org>
> To: <binutils@sourceware.org>
> ---
>  config.sub | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/config.sub b/config.sub
> index 2c6a07ab3c34eabed8318ec0a37c0cc23b77a63f..63ff958ec125e543674e9b261d5e5bb2fa749c4e 100755
> --- a/config.sub
> +++ b/config.sub
> @@ -1205,6 +1205,7 @@ case $cpu-$vendor in
>  			| hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
>  			| hexagon \
>  			| i370 | i*86 | i860 | i960 | ia16 | ia64 \
> +			| intelgt \
>  			| ip2k | iq2000 \
>  			| k1om \
>  			| kvx \
> 

Patch 3 enables ld, but I can't spot any gas enabling (sadly the cover letter
wasn't Cc-ed to the binutils list). Don't you further need to exclude gas from
attempts of configuring?

I'm also puzzled by that difference: The series supposedly is about enabling
gdb. Why enable ld there? Just because it's (seemingly) easy?

Jan

^ permalink raw reply	[flat|nested] 87+ messages in thread

* RE: [PATCH v2 04/47] opcodes: add intelgt as a configuration
  2024-12-16  7:44   ` Jan Beulich
@ 2024-12-17 18:47     ` Aktemur, Tankut Baris
  2024-12-18  7:22       ` Jan Beulich
  0 siblings, 1 reply; 87+ messages in thread
From: Aktemur, Tankut Baris @ 2024-12-17 18:47 UTC (permalink / raw)
  To: Beulich, Jan, gdb-patches, binutils; +Cc: Metzger, Markus T

On Monday, December 16, 2024 8:44 AM, Jan Beulich wrote:
> On 13.12.2024 16:59, Tankut Baris Aktemur wrote:
> > Intel GT does not use the opcodes lib for disassembling instructions.
> 
> How else is disassembly going to work?
> 
> Jan

For disassembly, libiga (IGA library from Intel Graphics Compiler [1]) is used.
The patch that adds disassembly support is

  https://sourceware.org/pipermail/gdb-patches/2024-December/214036.html

Regards
-Baris

[1]: https://github.com/intel/intel-graphics-compiler


Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* RE: [PATCH v2 01/47] gdb, intelgt: add intelgt as a basic machine
  2024-12-16  7:53   ` Jan Beulich
@ 2024-12-17 18:48     ` Aktemur, Tankut Baris
  2024-12-18  7:19       ` Jan Beulich
  0 siblings, 1 reply; 87+ messages in thread
From: Aktemur, Tankut Baris @ 2024-12-17 18:48 UTC (permalink / raw)
  To: Beulich, Jan, config-patches, gdb-patches, binutils; +Cc: Metzger, Markus T

On Monday, December 16, 2024 8:54 AM, Jan Beulich wrote:
> On 13.12.2024 16:59, Tankut Baris Aktemur wrote:
> > Add 'intelgt' as a basic machine to config.sub.
> >
> > To: <config-patches@gnu.org>
> > To: <binutils@sourceware.org>
> > ---
> >  config.sub | 1 +
> >  1 file changed, 1 insertion(+)
> >
> > diff --git a/config.sub b/config.sub
> > index 2c6a07ab3c34eabed8318ec0a37c0cc23b77a63f..63ff958ec125e543674e9b261d5e5bb2fa749c4e
> 100755
> > --- a/config.sub
> > +++ b/config.sub
> > @@ -1205,6 +1205,7 @@ case $cpu-$vendor in
> >  			| hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
> >  			| hexagon \
> >  			| i370 | i*86 | i860 | i960 | ia16 | ia64 \
> > +			| intelgt \
> >  			| ip2k | iq2000 \
> >  			| k1om \
> >  			| kvx \
> >
> 
> Patch 3 enables ld, but I can't spot any gas enabling (sadly the cover letter
> wasn't Cc-ed to the binutils list).

Sorry, this is my bad.  I'll Cc binutils in the next revision submission.
For the time being, the link to the cover letter is

  https://sourceware.org/pipermail/gdb-patches/2024-December/214029.html

> Don't you further need to exclude gas from
> attempts of configuring?
> 
> I'm also puzzled by that difference: The series supposedly is about enabling
> gdb. Why enable ld there? Just because it's (seemingly) easy?
> 
> Jan

Without enabling ld, when configured as

  <src>/configure --enable-targets="intelgt-elf"

'make' gives

  *** ld does not support target intelgt-unknown-elf
  *** see ld/configure.tgt for supported targets

Regards
-Baris


Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [PATCH v2 01/47] gdb, intelgt: add intelgt as a basic machine
  2024-12-17 18:48     ` Aktemur, Tankut Baris
@ 2024-12-18  7:19       ` Jan Beulich
  2024-12-20  9:55         ` Aktemur, Tankut Baris
  0 siblings, 1 reply; 87+ messages in thread
From: Jan Beulich @ 2024-12-18  7:19 UTC (permalink / raw)
  To: Aktemur, Tankut Baris
  Cc: Metzger, Markus T, config-patches, gdb-patches, binutils

On 17.12.2024 19:48, Aktemur, Tankut Baris wrote:
> On Monday, December 16, 2024 8:54 AM, Jan Beulich wrote:
>> On 13.12.2024 16:59, Tankut Baris Aktemur wrote:
>>> Add 'intelgt' as a basic machine to config.sub.
>>>
>>> To: <config-patches@gnu.org>
>>> To: <binutils@sourceware.org>
>>> ---
>>>  config.sub | 1 +
>>>  1 file changed, 1 insertion(+)
>>>
>>> diff --git a/config.sub b/config.sub
>>> index 2c6a07ab3c34eabed8318ec0a37c0cc23b77a63f..63ff958ec125e543674e9b261d5e5bb2fa749c4e
>> 100755
>>> --- a/config.sub
>>> +++ b/config.sub
>>> @@ -1205,6 +1205,7 @@ case $cpu-$vendor in
>>>  			| hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
>>>  			| hexagon \
>>>  			| i370 | i*86 | i860 | i960 | ia16 | ia64 \
>>> +			| intelgt \
>>>  			| ip2k | iq2000 \
>>>  			| k1om \
>>>  			| kvx \
>>>
>>
>> Patch 3 enables ld, but I can't spot any gas enabling (sadly the cover letter
>> wasn't Cc-ed to the binutils list).
> 
> Sorry, this is my bad.  I'll Cc binutils in the next revision submission.
> For the time being, the link to the cover letter is
> 
>   https://sourceware.org/pipermail/gdb-patches/2024-December/214029.html
> 
>> Don't you further need to exclude gas from
>> attempts of configuring?
>>
>> I'm also puzzled by that difference: The series supposedly is about enabling
>> gdb. Why enable ld there? Just because it's (seemingly) easy?
>>
>> Jan
> 
> Without enabling ld, when configured as
> 
>   <src>/configure --enable-targets="intelgt-elf"
> 
> 'make' gives
> 
>   *** ld does not support target intelgt-unknown-elf
>   *** see ld/configure.tgt for supported targets

Like I think I said for gas, that imo wants dealing with by a top level
configure change, adding ld to noconfigtargets for intelgt.

Jan

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [PATCH v2 04/47] opcodes: add intelgt as a configuration
  2024-12-17 18:47     ` Aktemur, Tankut Baris
@ 2024-12-18  7:22       ` Jan Beulich
  2024-12-20  9:47         ` Aktemur, Tankut Baris
  0 siblings, 1 reply; 87+ messages in thread
From: Jan Beulich @ 2024-12-18  7:22 UTC (permalink / raw)
  To: Aktemur, Tankut Baris; +Cc: Metzger, Markus T, gdb-patches, binutils

On 17.12.2024 19:47, Aktemur, Tankut Baris wrote:
> On Monday, December 16, 2024 8:44 AM, Jan Beulich wrote:
>> On 13.12.2024 16:59, Tankut Baris Aktemur wrote:
>>> Intel GT does not use the opcodes lib for disassembling instructions.
>>
>> How else is disassembly going to work?
> 
> For disassembly, libiga (IGA library from Intel Graphics Compiler [1]) is used.
> The patch that adds disassembly support is
> 
>   https://sourceware.org/pipermail/gdb-patches/2024-December/214036.html

That's (a) interesting and (b) covering only gdb; what about objdump?

Jan

^ permalink raw reply	[flat|nested] 87+ messages in thread

* RE: [PATCH v2 04/47] opcodes: add intelgt as a configuration
  2024-12-18  7:22       ` Jan Beulich
@ 2024-12-20  9:47         ` Aktemur, Tankut Baris
  2025-01-03  4:46           ` Simon Marchi
  0 siblings, 1 reply; 87+ messages in thread
From: Aktemur, Tankut Baris @ 2024-12-20  9:47 UTC (permalink / raw)
  To: Beulich, Jan, gdb-patches, binutils; +Cc: Metzger, Markus T, Simon Marchi

On Wednesday, December 18, 2024 8:22 AM, Jan Beulich wrote:
> On 17.12.2024 19:47, Aktemur, Tankut Baris wrote:
> > On Monday, December 16, 2024 8:44 AM, Jan Beulich wrote:
> >> On 13.12.2024 16:59, Tankut Baris Aktemur wrote:
> >>> Intel GT does not use the opcodes lib for disassembling instructions.
> >>
> >> How else is disassembly going to work?
> >
> > For disassembly, libiga (IGA library from Intel Graphics Compiler [1]) is used.
> > The patch that adds disassembly support is
> >
> >   https://sourceware.org/pipermail/gdb-patches/2024-December/214036.html
> 
> That's (a) interesting and (b) covering only gdb; what about objdump?
> 
> Jan

Our case is similar to AMD GPU (ROCm) debugging, for which there is
already support in upstream GDB.  For disassembling, the amdgpu target
also uses a separate disassembler that is part of their debug API
library:

  https://inbox.sourceware.org/binutils/20220315194303.3716792-3-simon.marchi@polymtl.ca/
  https://rocm.docs.amd.com/projects/ROCdbgapi/en/latest/doxygen/html/group__architecture__group.html#ga1958899dde4a98c13980e1cb7d657acd

I believe the amdgpu target has similar configuration restrictions
with our target.  Adding Simon to Cc to confirm.

Thanks,
-Baris


Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* RE: [PATCH v2 01/47] gdb, intelgt: add intelgt as a basic machine
  2024-12-18  7:19       ` Jan Beulich
@ 2024-12-20  9:55         ` Aktemur, Tankut Baris
  2025-02-03 17:17           ` Aktemur, Tankut Baris
  0 siblings, 1 reply; 87+ messages in thread
From: Aktemur, Tankut Baris @ 2024-12-20  9:55 UTC (permalink / raw)
  To: Beulich, Jan, config-patches, gdb-patches, binutils; +Cc: Metzger, Markus T

Hi,

On Wednesday, December 18, 2024 8:20 AM, Jan Beulich wrote:
> On 17.12.2024 19:48, Aktemur, Tankut Baris wrote:
> > On Monday, December 16, 2024 8:54 AM, Jan Beulich wrote:
> >> On 13.12.2024 16:59, Tankut Baris Aktemur wrote:
> >>> Add 'intelgt' as a basic machine to config.sub.
> >>>
> >>> To: <config-patches@gnu.org>
> >>> To: <binutils@sourceware.org>
> >>> ---
> >>>  config.sub | 1 +
> >>>  1 file changed, 1 insertion(+)
> >>>
> >>> diff --git a/config.sub b/config.sub
> >>> index
> 2c6a07ab3c34eabed8318ec0a37c0cc23b77a63f..63ff958ec125e543674e9b261d5e5bb2fa749c4e
> >> 100755
> >>> --- a/config.sub
> >>> +++ b/config.sub
> >>> @@ -1205,6 +1205,7 @@ case $cpu-$vendor in
> >>>  			| hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
> >>>  			| hexagon \
> >>>  			| i370 | i*86 | i860 | i960 | ia16 | ia64 \
> >>> +			| intelgt \
> >>>  			| ip2k | iq2000 \
> >>>  			| k1om \
> >>>  			| kvx \
> >>>
> >>
> >> Patch 3 enables ld, but I can't spot any gas enabling (sadly the cover letter
> >> wasn't Cc-ed to the binutils list).
> >
> > Sorry, this is my bad.  I'll Cc binutils in the next revision submission.
> > For the time being, the link to the cover letter is
> >
> >   https://sourceware.org/pipermail/gdb-patches/2024-December/214029.html
> >
> >> Don't you further need to exclude gas from
> >> attempts of configuring?
> >>
> >> I'm also puzzled by that difference: The series supposedly is about enabling
> >> gdb. Why enable ld there? Just because it's (seemingly) easy?
> >>
> >> Jan
> >
> > Without enabling ld, when configured as
> >
> >   <src>/configure --enable-targets="intelgt-elf"
> >
> > 'make' gives
> >
> >   *** ld does not support target intelgt-unknown-elf
> >   *** see ld/configure.tgt for supported targets
> 
> Like I think I said for gas, that imo wants dealing with by a top level
> configure change, adding ld to noconfigtargets for intelgt.

Our overall goal at this step is to enable GDB.

A GPU workload executes by being submitted to the device from a
host/native process.  Therefore, although enabling GDB only for the
GPU target is possible, in general it needs to be enabled in addition
to the native target.  That is, it makes sense that a x86-64 target is
the primary one whereas the intelgt target is enabled as a secondary
target, so that GDB is able to debug native processes as usual, plus
the GPU workloads.

The $noconfigdirs approach works for the primary target only.  Hence,
we cannot use it for an 'enabled-targets' target.

Another approach we can take is to not touch ld and let it fail.  The
expectation would be then:

  1. either configure GDB with "--disable-ld --disable-gas ..." and
     then use "make" to build it, or

  2. configure without disabling components but then use "make
     all-gdb" to build.

Our case is in fact similar to AMD GPU (ROCm) debug support in GDB.
The online documentation at

  https://rocm.docs.amd.com/projects/ROCgdb/en/latest/install/installation.html#build

suggests using "--disable-ld --disable-gas ...".  Otherwise ld would
fail with

  *** ld does not support target amdgcn-amd-amdhsa
  *** see ld/configure.tgt for supported targets

Regards,
-Baris



Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* RE: [PATCH v2 31/47] gdbserver: dump 'xx...x' in collect_register_as_string for unavailable register
  2024-12-13 15:59 ` [PATCH v2 31/47] gdbserver: dump 'xx...x' in collect_register_as_string for unavailable register Tankut Baris Aktemur
@ 2024-12-23 11:38   ` Aktemur, Tankut Baris
  2024-12-23 13:47     ` Luis Machado
  0 siblings, 1 reply; 87+ messages in thread
From: Aktemur, Tankut Baris @ 2024-12-23 11:38 UTC (permalink / raw)
  To: gdb-patches, Luis Machado; +Cc: Metzger, Markus T

On Friday, December 13, 2024 5:00 PM, Aktemur, Tankut Baris wrote:
> Fix 'collect_register_as_string' so that unavailable registers are
> dumped as 'xx...x' instead of arbitrary values.  This gives the
> opportunity that we can reuse 'collect_register_as_string' in
> 'registers_to_string' for additional code simplification.

This patch was previously reviewed by Luis, but I forgot to add
his trailer:

Reviewed-By:  Luis Machado  <luis.machado@arm.com>

I'm including the trailer in the local branch.

The review was here:
https://inbox.sourceware.org/gdb-patches/75397afd-7517-4aab-8f27-6721dcc56b28@arm.com/

Regards,
-Baris


Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [PATCH v2 31/47] gdbserver: dump 'xx...x' in collect_register_as_string for unavailable register
  2024-12-23 11:38   ` Aktemur, Tankut Baris
@ 2024-12-23 13:47     ` Luis Machado
  0 siblings, 0 replies; 87+ messages in thread
From: Luis Machado @ 2024-12-23 13:47 UTC (permalink / raw)
  To: Aktemur, Tankut Baris, gdb-patches; +Cc: Metzger, Markus T

On 12/23/24 11:38, Aktemur, Tankut Baris wrote:
> On Friday, December 13, 2024 5:00 PM, Aktemur, Tankut Baris wrote:
>> Fix 'collect_register_as_string' so that unavailable registers are
>> dumped as 'xx...x' instead of arbitrary values.  This gives the
>> opportunity that we can reuse 'collect_register_as_string' in
>> 'registers_to_string' for additional code simplification.
> 
> This patch was previously reviewed by Luis, but I forgot to add
> his trailer:
> 
> Reviewed-By:  Luis Machado  <luis.machado@arm.com>
> 
> I'm including the trailer in the local branch.
> 
> The review was here:
> https://inbox.sourceware.org/gdb-patches/75397afd-7517-4aab-8f27-6721dcc56b28@arm.com/
> 
> Regards,
> -Baris

Thanks. Don't worry. That's not a big deal.


^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [PATCH v2 04/47] opcodes: add intelgt as a configuration
  2024-12-20  9:47         ` Aktemur, Tankut Baris
@ 2025-01-03  4:46           ` Simon Marchi
  2025-02-03 17:13             ` Aktemur, Tankut Baris
  0 siblings, 1 reply; 87+ messages in thread
From: Simon Marchi @ 2025-01-03  4:46 UTC (permalink / raw)
  To: Aktemur, Tankut Baris, Beulich, Jan, gdb-patches, binutils
  Cc: Metzger, Markus T



On 2024-12-20 04:47, Aktemur, Tankut Baris wrote:
> On Wednesday, December 18, 2024 8:22 AM, Jan Beulich wrote:
>> On 17.12.2024 19:47, Aktemur, Tankut Baris wrote:
>>> On Monday, December 16, 2024 8:44 AM, Jan Beulich wrote:
>>>> On 13.12.2024 16:59, Tankut Baris Aktemur wrote:
>>>>> Intel GT does not use the opcodes lib for disassembling instructions.
>>>>
>>>> How else is disassembly going to work?
>>>
>>> For disassembly, libiga (IGA library from Intel Graphics Compiler [1]) is used.
>>> The patch that adds disassembly support is
>>>
>>>   https://sourceware.org/pipermail/gdb-patches/2024-December/214036.html
>>
>> That's (a) interesting and (b) covering only gdb; what about objdump?
>>
>> Jan
> 
> Our case is similar to AMD GPU (ROCm) debugging, for which there is
> already support in upstream GDB.  For disassembling, the amdgpu target
> also uses a separate disassembler that is part of their debug API
> library:
> 
>   https://inbox.sourceware.org/binutils/20220315194303.3716792-3-simon.marchi@polymtl.ca/
>   https://rocm.docs.amd.com/projects/ROCdbgapi/en/latest/doxygen/html/group__architecture__group.html#ga1958899dde4a98c13980e1cb7d657acd
> 
> I believe the amdgpu target has similar configuration restrictions
> with our target.  Adding Simon to Cc to confirm.
> 
> Thanks,
> -Baris

As far as I know, what Baris says is true.

Simon

^ permalink raw reply	[flat|nested] 87+ messages in thread

* RE: [PATCH v2 04/47] opcodes: add intelgt as a configuration
  2025-01-03  4:46           ` Simon Marchi
@ 2025-02-03 17:13             ` Aktemur, Tankut Baris
  2025-02-04  7:07               ` Jan Beulich
  0 siblings, 1 reply; 87+ messages in thread
From: Aktemur, Tankut Baris @ 2025-02-03 17:13 UTC (permalink / raw)
  To: Beulich, Jan, gdb-patches, binutils; +Cc: Metzger, Markus T, Simon Marchi

Hello Jan,

On Friday, January 3, 2025 5:46 AM, Simon Marchi wrote:
> On 2024-12-20 04:47, Aktemur, Tankut Baris wrote:
> > On Wednesday, December 18, 2024 8:22 AM, Jan Beulich wrote:
> >> On 17.12.2024 19:47, Aktemur, Tankut Baris wrote:
> >>> On Monday, December 16, 2024 8:44 AM, Jan Beulich wrote:
> >>>> On 13.12.2024 16:59, Tankut Baris Aktemur wrote:
> >>>>> Intel GT does not use the opcodes lib for disassembling instructions.
> >>>>
> >>>> How else is disassembly going to work?
> >>>
> >>> For disassembly, libiga (IGA library from Intel Graphics Compiler [1]) is used.
> >>> The patch that adds disassembly support is
> >>>
> >>>   https://sourceware.org/pipermail/gdb-patches/2024-December/214036.html
> >>
> >> That's (a) interesting and (b) covering only gdb; what about objdump?
> >>
> >> Jan
> >
> > Our case is similar to AMD GPU (ROCm) debugging, for which there is
> > already support in upstream GDB.  For disassembling, the amdgpu target
> > also uses a separate disassembler that is part of their debug API
> > library:
> >
> >   https://inbox.sourceware.org/binutils/20220315194303.3716792-3-simon.marchi@polymtl.ca/
> >
> https://rocm.docs.amd.com/projects/ROCdbgapi/en/latest/doxygen/html/group__architecture__gro
> up.html#ga1958899dde4a98c13980e1cb7d657acd
> >
> > I believe the amdgpu target has similar configuration restrictions
> > with our target.  Adding Simon to Cc to confirm.
> >
> > Thanks,
> > -Baris
> 
> As far as I know, what Baris says is true.
> 
> Simon

Any further comments on this?

Regards,
-Baris


Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* RE: [PATCH v2 01/47] gdb, intelgt: add intelgt as a basic machine
  2024-12-20  9:55         ` Aktemur, Tankut Baris
@ 2025-02-03 17:17           ` Aktemur, Tankut Baris
  2025-02-04  7:06             ` Jan Beulich
  0 siblings, 1 reply; 87+ messages in thread
From: Aktemur, Tankut Baris @ 2025-02-03 17:17 UTC (permalink / raw)
  To: Beulich, Jan, config-patches, gdb-patches, binutils; +Cc: Metzger, Markus T

Hello Jan,

On Friday, December 20, 2024 10:55 AM, Aktemur, Tankut Baris wrote:
> Hi,
> 
> On Wednesday, December 18, 2024 8:20 AM, Jan Beulich wrote:
> > On 17.12.2024 19:48, Aktemur, Tankut Baris wrote:
> > > On Monday, December 16, 2024 8:54 AM, Jan Beulich wrote:
> > >> On 13.12.2024 16:59, Tankut Baris Aktemur wrote:
> > >>> Add 'intelgt' as a basic machine to config.sub.
> > >>>
> > >>> To: <config-patches@gnu.org>
> > >>> To: <binutils@sourceware.org>
> > >>> ---
> > >>>  config.sub | 1 +
> > >>>  1 file changed, 1 insertion(+)
> > >>>
> > >>> diff --git a/config.sub b/config.sub
> > >>> index
> > 2c6a07ab3c34eabed8318ec0a37c0cc23b77a63f..63ff958ec125e543674e9b261d5e5bb2fa749c4e
> > >> 100755
> > >>> --- a/config.sub
> > >>> +++ b/config.sub
> > >>> @@ -1205,6 +1205,7 @@ case $cpu-$vendor in
> > >>>  			| hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
> > >>>  			| hexagon \
> > >>>  			| i370 | i*86 | i860 | i960 | ia16 | ia64 \
> > >>> +			| intelgt \
> > >>>  			| ip2k | iq2000 \
> > >>>  			| k1om \
> > >>>  			| kvx \
> > >>>
> > >>
> > >> Patch 3 enables ld, but I can't spot any gas enabling (sadly the cover letter
> > >> wasn't Cc-ed to the binutils list).
> > >
> > > Sorry, this is my bad.  I'll Cc binutils in the next revision submission.
> > > For the time being, the link to the cover letter is
> > >
> > >   https://sourceware.org/pipermail/gdb-patches/2024-December/214029.html
> > >
> > >> Don't you further need to exclude gas from
> > >> attempts of configuring?
> > >>
> > >> I'm also puzzled by that difference: The series supposedly is about enabling
> > >> gdb. Why enable ld there? Just because it's (seemingly) easy?
> > >>
> > >> Jan
> > >
> > > Without enabling ld, when configured as
> > >
> > >   <src>/configure --enable-targets="intelgt-elf"
> > >
> > > 'make' gives
> > >
> > >   *** ld does not support target intelgt-unknown-elf
> > >   *** see ld/configure.tgt for supported targets
> >
> > Like I think I said for gas, that imo wants dealing with by a top level
> > configure change, adding ld to noconfigtargets for intelgt.
> 
> Our overall goal at this step is to enable GDB.
> 
> A GPU workload executes by being submitted to the device from a
> host/native process.  Therefore, although enabling GDB only for the
> GPU target is possible, in general it needs to be enabled in addition
> to the native target.  That is, it makes sense that a x86-64 target is
> the primary one whereas the intelgt target is enabled as a secondary
> target, so that GDB is able to debug native processes as usual, plus
> the GPU workloads.
> 
> The $noconfigdirs approach works for the primary target only.  Hence,
> we cannot use it for an 'enabled-targets' target.
> 
> Another approach we can take is to not touch ld and let it fail.  The
> expectation would be then:
> 
>   1. either configure GDB with "--disable-ld --disable-gas ..." and
>      then use "make" to build it, or
> 
>   2. configure without disabling components but then use "make
>      all-gdb" to build.
> 
> Our case is in fact similar to AMD GPU (ROCm) debug support in GDB.
> The online documentation at
> 
>   https://rocm.docs.amd.com/projects/ROCgdb/en/latest/install/installation.html#build
> 
> suggests using "--disable-ld --disable-gas ...".  Otherwise ld would
> fail with
> 
>   *** ld does not support target amdgcn-amd-amdhsa
>   *** see ld/configure.tgt for supported targets

Any further comments?

Regards,
-Baris


Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [PATCH v2 01/47] gdb, intelgt: add intelgt as a basic machine
  2025-02-03 17:17           ` Aktemur, Tankut Baris
@ 2025-02-04  7:06             ` Jan Beulich
  0 siblings, 0 replies; 87+ messages in thread
From: Jan Beulich @ 2025-02-04  7:06 UTC (permalink / raw)
  To: Aktemur, Tankut Baris
  Cc: Metzger, Markus T, config-patches, gdb-patches, binutils

On 03.02.2025 18:17, Aktemur, Tankut Baris wrote:
> Hello Jan,
> 
> On Friday, December 20, 2024 10:55 AM, Aktemur, Tankut Baris wrote:
>> Hi,
>>
>> On Wednesday, December 18, 2024 8:20 AM, Jan Beulich wrote:
>>> On 17.12.2024 19:48, Aktemur, Tankut Baris wrote:
>>>> On Monday, December 16, 2024 8:54 AM, Jan Beulich wrote:
>>>>> On 13.12.2024 16:59, Tankut Baris Aktemur wrote:
>>>>>> Add 'intelgt' as a basic machine to config.sub.
>>>>>>
>>>>>> To: <config-patches@gnu.org>
>>>>>> To: <binutils@sourceware.org>
>>>>>> ---
>>>>>>  config.sub | 1 +
>>>>>>  1 file changed, 1 insertion(+)
>>>>>>
>>>>>> diff --git a/config.sub b/config.sub
>>>>>> index
>>> 2c6a07ab3c34eabed8318ec0a37c0cc23b77a63f..63ff958ec125e543674e9b261d5e5bb2fa749c4e
>>>>> 100755
>>>>>> --- a/config.sub
>>>>>> +++ b/config.sub
>>>>>> @@ -1205,6 +1205,7 @@ case $cpu-$vendor in
>>>>>>  			| hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
>>>>>>  			| hexagon \
>>>>>>  			| i370 | i*86 | i860 | i960 | ia16 | ia64 \
>>>>>> +			| intelgt \
>>>>>>  			| ip2k | iq2000 \
>>>>>>  			| k1om \
>>>>>>  			| kvx \
>>>>>>
>>>>>
>>>>> Patch 3 enables ld, but I can't spot any gas enabling (sadly the cover letter
>>>>> wasn't Cc-ed to the binutils list).
>>>>
>>>> Sorry, this is my bad.  I'll Cc binutils in the next revision submission.
>>>> For the time being, the link to the cover letter is
>>>>
>>>>   https://sourceware.org/pipermail/gdb-patches/2024-December/214029.html
>>>>
>>>>> Don't you further need to exclude gas from
>>>>> attempts of configuring?
>>>>>
>>>>> I'm also puzzled by that difference: The series supposedly is about enabling
>>>>> gdb. Why enable ld there? Just because it's (seemingly) easy?
>>>>>
>>>>> Jan
>>>>
>>>> Without enabling ld, when configured as
>>>>
>>>>   <src>/configure --enable-targets="intelgt-elf"
>>>>
>>>> 'make' gives
>>>>
>>>>   *** ld does not support target intelgt-unknown-elf
>>>>   *** see ld/configure.tgt for supported targets
>>>
>>> Like I think I said for gas, that imo wants dealing with by a top level
>>> configure change, adding ld to noconfigtargets for intelgt.
>>
>> Our overall goal at this step is to enable GDB.
>>
>> A GPU workload executes by being submitted to the device from a
>> host/native process.  Therefore, although enabling GDB only for the
>> GPU target is possible, in general it needs to be enabled in addition
>> to the native target.  That is, it makes sense that a x86-64 target is
>> the primary one whereas the intelgt target is enabled as a secondary
>> target, so that GDB is able to debug native processes as usual, plus
>> the GPU workloads.
>>
>> The $noconfigdirs approach works for the primary target only.  Hence,
>> we cannot use it for an 'enabled-targets' target.
>>
>> Another approach we can take is to not touch ld and let it fail.  The
>> expectation would be then:
>>
>>   1. either configure GDB with "--disable-ld --disable-gas ..." and
>>      then use "make" to build it, or
>>
>>   2. configure without disabling components but then use "make
>>      all-gdb" to build.
>>
>> Our case is in fact similar to AMD GPU (ROCm) debug support in GDB.
>> The online documentation at
>>
>>   https://rocm.docs.amd.com/projects/ROCgdb/en/latest/install/installation.html#build
>>
>> suggests using "--disable-ld --disable-gas ...".  Otherwise ld would
>> fail with
>>
>>   *** ld does not support target amdgcn-amd-amdhsa
>>   *** see ld/configure.tgt for supported targets
> 
> Any further comments?

No; as you copy pre-existing behavior, I'm okay(ish).

Jan

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [PATCH v2 04/47] opcodes: add intelgt as a configuration
  2025-02-03 17:13             ` Aktemur, Tankut Baris
@ 2025-02-04  7:07               ` Jan Beulich
  0 siblings, 0 replies; 87+ messages in thread
From: Jan Beulich @ 2025-02-04  7:07 UTC (permalink / raw)
  To: Aktemur, Tankut Baris
  Cc: Metzger, Markus T, Simon Marchi, gdb-patches, binutils

On 03.02.2025 18:13, Aktemur, Tankut Baris wrote:
> Hello Jan,
> 
> On Friday, January 3, 2025 5:46 AM, Simon Marchi wrote:
>> On 2024-12-20 04:47, Aktemur, Tankut Baris wrote:
>>> On Wednesday, December 18, 2024 8:22 AM, Jan Beulich wrote:
>>>> On 17.12.2024 19:47, Aktemur, Tankut Baris wrote:
>>>>> On Monday, December 16, 2024 8:44 AM, Jan Beulich wrote:
>>>>>> On 13.12.2024 16:59, Tankut Baris Aktemur wrote:
>>>>>>> Intel GT does not use the opcodes lib for disassembling instructions.
>>>>>>
>>>>>> How else is disassembly going to work?
>>>>>
>>>>> For disassembly, libiga (IGA library from Intel Graphics Compiler [1]) is used.
>>>>> The patch that adds disassembly support is
>>>>>
>>>>>   https://sourceware.org/pipermail/gdb-patches/2024-December/214036.html
>>>>
>>>> That's (a) interesting and (b) covering only gdb; what about objdump?
>>>>
>>>> Jan
>>>
>>> Our case is similar to AMD GPU (ROCm) debugging, for which there is
>>> already support in upstream GDB.  For disassembling, the amdgpu target
>>> also uses a separate disassembler that is part of their debug API
>>> library:
>>>
>>>   https://inbox.sourceware.org/binutils/20220315194303.3716792-3-simon.marchi@polymtl.ca/
>>>
>> https://rocm.docs.amd.com/projects/ROCdbgapi/en/latest/doxygen/html/group__architecture__gro
>> up.html#ga1958899dde4a98c13980e1cb7d657acd
>>>
>>> I believe the amdgpu target has similar configuration restrictions
>>> with our target.  Adding Simon to Cc to confirm.
>>>
>>> Thanks,
>>> -Baris
>>
>> As far as I know, what Baris says is true.
>>
>> Simon
> 
> Any further comments on this?

As with the other patch - since you copy pre-existing behavior, I'm okay(ish).

Jan

^ permalink raw reply	[flat|nested] 87+ messages in thread

* RE: [PATCH v2 00/47] A new target to debug Intel GPUs
  2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
                   ` (46 preceding siblings ...)
  2024-12-13 16:00 ` [PATCH v2 47/47] testsuite, sycl: test canceling a stepping flow Tankut Baris Aktemur
@ 2025-02-07 10:18 ` Aktemur, Tankut Baris
  2025-05-08  7:40   ` Aktemur, Tankut Baris
  2025-07-03 12:55   ` Aktemur, Tankut Baris
  47 siblings, 2 replies; 87+ messages in thread
From: Aktemur, Tankut Baris @ 2025-02-07 10:18 UTC (permalink / raw)
  To: gdb-patches; +Cc: Metzger, Markus T, binutils developers

On Friday, December 13, 2024 4:59 PM, Aktemur, Tankut Baris wrote:
> Hello,
> 
> We (Intel) would like to submit patches to enable fundamental debug
> support for Intel GPU devices.
...
> 
> 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 upstreamed) that bring
> (1) SIMD 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.

Hello,

For those who might be interested, below is a link to the instructions
that explain how to build the debug sw stack (the kernel-mode and
user-mode drivers, plus GDB) from the patches submitted to upstream.

  https://gitlab.freedesktop.org/miku/kernel

Regards,
-Baris


Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* RE: [PATCH v2 00/47] A new target to debug Intel GPUs
  2025-02-07 10:18 ` [PATCH v2 00/47] A new target to debug Intel GPUs Aktemur, Tankut Baris
@ 2025-05-08  7:40   ` Aktemur, Tankut Baris
  2025-05-26  8:03     ` Aktemur, Tankut Baris
  2025-07-03 12:55   ` Aktemur, Tankut Baris
  1 sibling, 1 reply; 87+ messages in thread
From: Aktemur, Tankut Baris @ 2025-05-08  7:40 UTC (permalink / raw)
  To: gdb-patches; +Cc: Metzger, Markus T

> On Friday, December 13, 2024 4:59 PM, Aktemur, Tankut Baris wrote:
> > Hello,
> >
> > We (Intel) would like to submit patches to enable fundamental debug
> > support for Intel GPU devices.

Kindly pinging.

Thank you.
-Baris

> >
> > 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 upstreamed) that bring
> > (1) SIMD 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.
> 
> Hello,
> 
> For those who might be interested, below is a link to the instructions
> that explain how to build the debug sw stack (the kernel-mode and
> user-mode drivers, plus GDB) from the patches submitted to upstream.
> 
>   https://gitlab.freedesktop.org/miku/kernel
> 
> Regards,
> -Baris


Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* RE: [PATCH v2 00/47] A new target to debug Intel GPUs
  2025-05-08  7:40   ` Aktemur, Tankut Baris
@ 2025-05-26  8:03     ` Aktemur, Tankut Baris
  2025-06-17 12:22       ` Aktemur, Tankut Baris
  0 siblings, 1 reply; 87+ messages in thread
From: Aktemur, Tankut Baris @ 2025-05-26  8:03 UTC (permalink / raw)
  To: gdb-patches; +Cc: Metzger, Markus T

> On Friday, December 13, 2024 4:59 PM, Aktemur, Tankut Baris wrote:
> > Hello,
> >
> > We (Intel) would like to submit patches to enable fundamental debug
> > support for Intel GPU devices.

Kindly pinging.

Thank you.
-Baris

> >
> > 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 upstreamed) that bring
> > (1) SIMD 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.
> 
> Hello,
> 
> For those who might be interested, below is a link to the instructions
> that explain how to build the debug sw stack (the kernel-mode and
> user-mode drivers, plus GDB) from the patches submitted to upstream.
> 
>   https://gitlab.freedesktop.org/miku/kernel
> 
> Regards,
> -Baris


Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* RE: [PATCH v2 00/47] A new target to debug Intel GPUs
  2025-05-26  8:03     ` Aktemur, Tankut Baris
@ 2025-06-17 12:22       ` Aktemur, Tankut Baris
  0 siblings, 0 replies; 87+ messages in thread
From: Aktemur, Tankut Baris @ 2025-06-17 12:22 UTC (permalink / raw)
  To: gdb-patches; +Cc: Metzger, Markus T

> On Friday, December 13, 2024 4:59 PM, Aktemur, Tankut Baris wrote:
> > Hello,
> >
> > We (Intel) would like to submit patches to enable fundamental debug
> > support for Intel GPU devices.

Kindly pinging.

Thank you.
-Baris

> >
> > 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 upstreamed) that bring
> > (1) SIMD 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.
> 
> Hello,
> 
> For those who might be interested, below is a link to the instructions
> that explain how to build the debug sw stack (the kernel-mode and
> user-mode drivers, plus GDB) from the patches submitted to upstream.
> 
>   https://gitlab.freedesktop.org/miku/kernel
> 
> Regards,
> -Baris


Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* RE: [PATCH v2 00/47] A new target to debug Intel GPUs
  2025-02-07 10:18 ` [PATCH v2 00/47] A new target to debug Intel GPUs Aktemur, Tankut Baris
  2025-05-08  7:40   ` Aktemur, Tankut Baris
@ 2025-07-03 12:55   ` Aktemur, Tankut Baris
  1 sibling, 0 replies; 87+ messages in thread
From: Aktemur, Tankut Baris @ 2025-07-03 12:55 UTC (permalink / raw)
  To: gdb-patches; +Cc: Metzger, Markus T

> On Friday, December 13, 2024 4:59 PM, Aktemur, Tankut Baris wrote:
> > Hello,
> >
> > We (Intel) would like to submit patches to enable fundamental debug
> > support for Intel GPU devices.

Kindly pinging.

(Yesterday was the anniversary of submission of v1 of this series.)

Thank you.
-Baris

> >
> > 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 upstreamed) that bring
> > (1) SIMD 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.
> 
> Hello,
> 
> For those who might be interested, below is a link to the instructions
> that explain how to build the debug sw stack (the kernel-mode and
> user-mode drivers, plus GDB) from the patches submitted to upstream.
> 
>   https://gitlab.freedesktop.org/miku/kernel
> 
> Regards,
> -Baris


Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [PATCH v2 06/47] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture
  2024-12-13 15:59 ` [PATCH v2 06/47] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture Tankut Baris Aktemur
@ 2025-07-08  2:43   ` Thiago Jung Bauermann
  2025-07-18 17:43     ` Aktemur, Tankut Baris
  0 siblings, 1 reply; 87+ messages in thread
From: Thiago Jung Bauermann @ 2025-07-08  2:43 UTC (permalink / raw)
  To: Tankut Baris Aktemur; +Cc: gdb-patches, Markus Metzger

Hello Baris,

Reviewing this patch series has been on my todo list for a long
time. I'll try to go through it this week.

Tankut Baris Aktemur <tankut.baris.aktemur@intel.com> writes:

> Introduce gdb/intelgt-tdep.c for the Intel GT target.  The target is
> defined in a future patch as a gdbserver low target implementation.
>
> Other than, for example, X86-64, IntelGT does not have a dedicated

I suggest using "Unlike" instead of "Other than" here.

> breakpoint instruction.  Instead, it has a breakpoint bit in each
> instruction.  Hence, any instruction can be used as a breakpoint
> instruction.

<snip>

> diff --git a/gdb/intelgt-tdep.c b/gdb/intelgt-tdep.c
> new file mode 100755
> index 0000000000000000000000000000000000000000..57c359bf355c5771db38b8d213f6681a043c2b33
> --- /dev/null
> +++ b/gdb/intelgt-tdep.c
> @@ -0,0 +1,980 @@
> +/* Target-dependent code for the Intel(R) Graphics Technology architecture.
> +
> +   Copyright (C) 2019-2024 Free Software Foundation, Inc.

Does the copyright on this file really start on 2019?
Also, reminder to update to 2025.

> +
> +   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 "frame-unwind.h"
> +#include "gdbsupport/gdb_obstack.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.  */
> +
> +struct regnum_range
> +{
> +  int start;
> +  int end;
> +};
> +
> +/* The encoding for XE version enumerates follows this pattern, which is
> +   aligned with the IGA encoding.  */
> +
> +#define XE_VERSION(MAJ, MIN) (((MAJ) << 24) | (MIN))
> +
> +/* Supported GDB GEN platforms.  */
> +
> +enum xe_version
> +{
> +  XE_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),
> +};
> +
> +/* Helper functions to request and translate the device id/version.  */
> +
> +[[maybe_unused]] static xe_version get_xe_version (unsigned int device_id);

If the definition of get_xe_version is guarded by "#ifdef
HAVE_LIBIGA64", then this prototype isn't needed.

Also, the definition of get_xe_version can be moved to patch 8 ("gdb,
intelgt: add disassemble feature for the Intel GT architecture."), which
uses it.

> +/* Data specific for this architecture.  */
> +
> +struct intelgt_gdbarch_tdep : gdbarch_tdep_base
> +{
> +  /* $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;
> +
> +  /* Cached $framedesc pseudo-register type.  */
> +  type *framedesc_type = nullptr;
> +
> +  /* Initialize ranges to -1 as "not-yet-set" indicator.  */
> +  intelgt_gdbarch_tdep ()
> +  {
> +    memset (&regset_ranges, -1, sizeof regset_ranges);
> +  }
> +
> +  /* 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;
> +  }
> +};
> +
> +/* The 'register_type' gdbarch method.  */
> +
> +static type *
> +intelgt_register_type (gdbarch *gdbarch, int regno)
> +{
> +  type *typ = tdesc_register_type (gdbarch, regno);
> +  return typ;

Minor nit: no need to declare typ, you can directly return the result of
the call to tdesc_register_type.

> +}
> +
> +/* 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[%zu:%zu] is outside the range of %s[%d:0]."),
> +	   regname, (offset + buffer.size () - 1), offset,
> +	   regname, (regsize - 1));

Shouldn't this error include error_message as well?

> +  register_status reg_status
> +    = regcache->cooked_read_part (regnum, offset, buffer);
> +
> +  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);
> +}
> +
> +static int
> +intelgt_pseudo_register_num (gdbarch *arch, const char *name);

If you move the definition of intelgt_pseudo_register_num here, you
won't need this prototype declaration.

> +/* 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",
> +    "scrbase",
> +    "genstbase",
> +    "sustbase",
> +    "blsustbase",
> +    "blsastbase",
> +    "scrbase2"
> +  };
> +
> +  intelgt_gdbarch_tdep *data
> +    = gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch);

Nit: the above fits in one line, no need to break into two.  There are
several occurrences of this in the file. I'll just comment on this one.

> +  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;
> +	    const char* name = sba_dwarf_reg_order [sba_num];
> +
> +	    return user_reg_map_name_to_regnum (gdbarch, name, -1);
> +	  }
> +	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 std::max (start_pc, post_prologue_pc);

Considering that both start_pc and post_prologue_pc are CORE_ADDR (an
unsigned type), checking whether post_prologue_pc is 0 is redundant:
std::max will return start_pc in that case.

> +    }
> +
> +  /* 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.");
> +}
> +
> +/* Callback function to unwind the $framedesc register.  */
> +
> +static value *
> +intelgt_dwarf2_prev_framedesc (const frame_info_ptr &this_frame,
> +			       void **this_cache, int regnum)
> +{
> +  gdbarch *gdbarch = get_frame_arch (this_frame);
> +  intelgt_gdbarch_tdep *data
> +    = gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch);
> +
> +  int actual_regnum = data->framedesc_base_regnum ();
> +
> +  /* Unwind the actual GRF register.  */
> +  return frame_unwind_register_value (this_frame, actual_regnum);
> +}
> +
> +/* 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");
> +  int framedesc_regnum = intelgt_pseudo_register_num (gdbarch, "framedesc");
> +
> +  if (regnum == ip_regnum)
> +    reg->how = DWARF2_FRAME_REG_RA;
> +  else if (regnum == gdbarch_sp_regnum (gdbarch))
> +    reg->how = DWARF2_FRAME_REG_CFA;
> +  /* We use special functions to unwind the $framedesc register.  */

s/special functions/a special function/

> +  else if (regnum == framedesc_regnum)
> +    {
> +      reg->how = DWARF2_FRAME_REG_FN;
> +      reg->loc.fn = intelgt_dwarf2_prev_framedesc;
> +    }
> +}
> +
> +/* 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);

There's no need to break the line above. Everything fits in one line.

> +  intelgt_debug_printf ("prev_ip: %s", paddress (gdbarch, prev_ip));
> +
> +  /* Program counter is $ip + $isabase.  Read directly from the
> +     regcache instead of unwinding, as the frame unwind info may
> +     simply be unavailable.  The isabase register does not change
> +     during kernel execution, so this must be safe.  */
> +  regcache *regcache = get_thread_regcache (inferior_thread ());
> +  CORE_ADDR isabase = intelgt_get_isabase (regcache);
> +
> +  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;
> +}
> +
> +static const struct frame_unwind intelgt_unwinder =
> +  {
> +    "intelgt prologue",
> +    NORMAL_FRAME,			/* type */
> +    default_frame_unwind_stop_reason,	/* stop_reason */
> +    intelgt_frame_this_id,		/* this_id */
> +    nullptr,				/* prev_register */
> +    nullptr,				/* unwind_data */
> +    default_frame_sniffer,		/* sniffer */
> +    nullptr,				/* dealloc_cache */
> +  };

This unwinder doesn't do much. Is it necessary? If so, I suggest a
comment explaining why.

> +
> +/* 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 I think 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;
> +  bp->shadow_len = intelgt::inst_length (inst);
> +
> +  /* 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);
> +  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));
> +
> +  /* Warn if we're inserting a permanent breakpoint.  */
> +  if (intelgt::has_breakpoint (bp->shadow_contents))
> +    warning (_("Re-inserting permanent breakpoint at %s."),
> +	     paddress (gdbarch, bp->placed_address));
> +
> +  /* See comment in mem-break.c on write_inferior_memory.  */

I wasn't able to find that comment. Has it been removed from GDB?

> +  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 I think 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;
> +    }
> +
> +  const bool is_bkpt = intelgt::has_breakpoint (inst);
> +
> +  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;
> +}
> +
> +/* 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 ());
> +}
> +
> +/* 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));

The error message should be surrounded by _() to mark it as
translatable.

> +
> +  intelgt_gdbarch_tdep *data
> +    = gdbarch_tdep<intelgt_gdbarch_tdep> (arch);
> +
> +  /* Instruction pointer is stored in CR0.2.  */
> +  uint32_t ip = pc - isabase;
> +  regcache->cooked_write_part (data->cr0_regnum, sizeof (uint32_t) * 2,
> +			       gdb::make_array_view ((gdb_byte *) &ip,
> +						     sizeof (uint32_t)));
> +}
> +
> +/* 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);

The error message should be surrounded by _() to mark it as
translatable.

> +  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);
> +  intelgt_gdbarch_tdep *data
> +    = gdbarch_tdep<intelgt_gdbarch_tdep> (arch);
> +
> +  if (strcmp (name, "framedesc") == 0)
> +    {
> +      if (data->framedesc_type != nullptr)
> +	return data->framedesc_type;
> +      type *frame = arch_composite_type (arch, "frame_desc", TYPE_CODE_STRUCT);
> +      append_composite_type_field (frame, "return_ip", bt->builtin_uint32);

Isn't it better to use a function pointer type? This is what x86_64 and
aarch64 do:

(gdb) ptype $pc
type = void (*)()

Though I see further below that pointer and address lengths in gdbarch
are set to 64 bits, so perhaps things aren't very straightforward in
this architecture...

> +      append_composite_type_field (frame, "return_callmask",
> +				   bt->builtin_uint32);
> +      append_composite_type_field (frame, "be_sp", bt->builtin_uint32);
> +      append_composite_type_field (frame, "be_fp", bt->builtin_uint32);
> +      append_composite_type_field (frame, "fe_fp", bt->builtin_uint64);
> +      append_composite_type_field (frame, "fe_sp", bt->builtin_uint64);

If the 'p' in the fields above mean "pointer", would a void pointer
work? This is what x86_64 and aarch64 do:

(gdb) ptype $sp
type = void *

> +      data->framedesc_type = frame;
> +      return frame;
> +    }
> +  else if (strcmp (name, "ip") == 0)
> +    return bt->builtin_uint32;

Same note about using a function pointer type as in return_ip above.

> +  return nullptr;
> +}

<snip>

> +/* 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 *gdbarch
> +    = gdbarch_alloc (&info,
> +		     gdbarch_tdep_up (new intelgt_gdbarch_tdep));

There are some calls to error () below. They'll leak gdbarch. The patch
adding disassemble support will also introduce some early returns with
calls to gdbarch_free (gdbarch).

You could instead use gdbarch_up and release () the value when returning
it at the end of this function so that it's automatically freed in case
of an early return:

  gdbarch_up gdbarch_u
    (gdbarch_alloc (&info, gdbarch_tdep_up (new intelgt_gdbarch_tdep)));
  gdbarch *gdbarch = gdbarch_u.get ();

Then at the end:

  return gdbarch_u.release ();

> +  intelgt_gdbarch_tdep *data
> +    = gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch);
> +
> +  /* Initialize register info.  */
> +  set_gdbarch_num_regs (gdbarch, 0);
> +  set_gdbarch_register_name (gdbarch, tdesc_register_name);
> +
> +  if (tdesc_has_registers (tdesc))
> +    {
> +      tdesc_arch_data_up tdesc_data = tdesc_data_alloc ();
> +
> +      /* First assign register numbers to all registers.  The
> +	 callback function will record any relevant metadata
> +	 about it in the intelgt_gdbarch_data instance to be
> +	 inspected after.  */
> +
> +      tdesc_use_registers (gdbarch, tdesc, std::move (tdesc_data),
> +			   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 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");

The error messages should be surrounded by _() to mark them as
translatable.

> +      /* Unconditionally enabled pseudo-registers:  */
> +      data->enabled_pseudo_regs.push_back ("ip");
> +      data->enabled_pseudo_regs.push_back ("framedesc");
> +
> +      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_ptr_bit (gdbarch, 64);
> +  set_gdbarch_addr_bit (gdbarch, 64);

I'm a bit surprised by these, considering that PC is 32 bits. If this is
correct, then it's worth adding an explanation about the discrepancy.
Is this why PC is uint32 instead of a pointer type?

> +  set_gdbarch_long_bit (gdbarch, 64);
> +
> +  set_gdbarch_register_type (gdbarch, intelgt_register_type);

Considering that intelgt_register_type just calls tdesc_register_type,
shouldn't this be set to tdesc_register_type directly?

> +  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_unwinder);
> +
> +  set_gdbarch_return_value_as_value (gdbarch, intelgt_return_value_as_value);
> +
> +  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;
> +}
> +
> +/* Dump the target specific data for this architecture.  */
> +
> +static void
> +intelgt_dump_tdep (gdbarch *gdbarch, ui_file *file)
> +{
> +  /* Implement target-specific print output if and
> +     when gdbarch_tdep is defined for this architecture.  */

From looking at gdbarch_dump in gdb/gdbarch-gen.c, you can just pass
nullptr to gdbarch_register instead of having to define a dummy function.

Though the comment isn't accurate. There is intelgt_gdbarch_tdep, but
perhaps it's not necessary to dump any of its values?

> +}
> +
> +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);
> +}
> +
> +void _initialize_intelgt_tdep ();
> +void
> +_initialize_intelgt_tdep ()

Update to current style using INIT_GDB_FILE macro.

> +{
> +  gdbarch_register (bfd_arch_intelgt, intelgt_gdbarch_init,
> +		    intelgt_dump_tdep);
> +
> +  /* 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);
> +}

-- 
Thiago

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [PATCH v2 05/47] gdb, arch, intelgt: add intelgt arch definitions
  2024-12-13 15:59 ` [PATCH v2 05/47] gdb, arch, intelgt: add intelgt arch definitions Tankut Baris Aktemur
@ 2025-07-08  3:03   ` Thiago Jung Bauermann
  2025-07-21 10:49     ` Aktemur, Tankut Baris
  0 siblings, 1 reply; 87+ messages in thread
From: Thiago Jung Bauermann @ 2025-07-08  3:03 UTC (permalink / raw)
  To: Tankut Baris Aktemur; +Cc: gdb-patches, Markus Metzger

Tankut Baris Aktemur <tankut.baris.aktemur@intel.com> writes:

> From: Markus Metzger <markus.t.metzger@intel.com>
>
> 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>
> ---
>  gdb/Makefile.in    |   1 +
>  gdb/arch/intelgt.c |  77 +++++++++++++++++++++++
>  gdb/arch/intelgt.h | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 253 insertions(+)

Just one nit below, but regardless:

Reviewed-By: Thiago Jung Bauermann <thiago.bauermann@linaro.org>

<snip>

> +static inline unsigned int
> +inst_length_compacted ()
> +{
> +  return COMPACT_INST_LENGTH;
> +}
> +
> +static inline unsigned int
> +inst_length_full ()
> +{
> +  return MAX_INST_LENGTH;
> +}

Not sure I see an advantage in these functions. I suggest removing them
and using the constants directly.

> +static inline unsigned int
> +inst_length (gdb::array_view<const gdb_byte> inst)
> +{
> +  return (is_compacted_inst (inst)
> +	  ? inst_length_compacted ()
> +	  : inst_length_full ());
> +}
> +
> +} /* namespace intelgt */
> +
> +#endif

-- 
Thiago

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [PATCH v2 07/47] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description
  2024-12-13 15:59 ` [PATCH v2 07/47] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description Tankut Baris Aktemur
  2024-12-13 16:45   ` Eli Zaretskii
@ 2025-07-08  4:04   ` Thiago Jung Bauermann
  2025-07-21 10:49     ` Aktemur, Tankut Baris
  1 sibling, 1 reply; 87+ messages in thread
From: Thiago Jung Bauermann @ 2025-07-08  4:04 UTC (permalink / raw)
  To: Tankut Baris Aktemur; +Cc: gdb-patches, Markus Metzger

Tankut Baris Aktemur <tankut.baris.aktemur@intel.com> writes:

> diff --git a/gdb/xml-tdesc.c b/gdb/xml-tdesc.c
> index 436c493d4f914eb71a80bcbc08dc2ddd38c72027..61cba42e9eee2ec4cc66e14a120c5bef909e48b0 100644
> --- a/gdb/xml-tdesc.c
> +++ b/gdb/xml-tdesc.c
> @@ -137,6 +137,65 @@ tdesc_end_compatible (struct gdb_xml_parser *parser,
>    tdesc_add_compatible (data->tdesc, arch);
>  }
>  
> +/* Handle the end of a <device> element and its value.  */

s/the end/the start/

> +static void
> +tdesc_start_device (struct gdb_xml_parser *parser,
> +		    const gdb_xml_element *element,
> +		    void *user_data, std::vector<gdb_xml_value> &attributes)
> +{

<snip>

> +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);
> +
> +  string_appendf (tmp, " family=\"%s\"", device->family.c_str ());
> +  string_appendf (tmp, " model=\"%s\"", device->model.c_str ());

For all the string attributes in device: isn't it better to check
whether the string is empty and only emit the attribute if it isn't?

> +  if (device->stepping.has_value ())
> +    string_appendf (tmp, " stepping=\"%d\"", *device->stepping);
> +
> +  string_appendf (tmp, " name=\"%s\"", device->name.c_str ());
> +  string_appendf (tmp, " pci-slot=\"%s\"", device->pci_slot.c_str ());
> +  string_appendf (tmp, " uuid=\"%s\"", device->uuid.c_str ());
> +
> +  if (device->total_cores.has_value ())
> +    string_appendf (tmp, " total-cores=\"%ld\"", *device->total_cores);
> +
> +  if (device->total_threads.has_value ())
> +    string_appendf (tmp, " total-threads=\"%ld\"", *device->total_threads);
> +
> +  if (device->subdevice_id.has_value ())
> +    string_appendf (tmp, " subdevice-id=\"%d\"", *device->subdevice_id);
> +
> +  string_appendf (tmp, "/>");
> +  add_line (tmp);
> +}
> +
>  /* See gdbsupport/tdesc.h.  */
>  
>  void
> diff --git a/gdbsupport/tdesc.h b/gdbsupport/tdesc.h
> index c9e7603369cbf986187908c2d1f8fbe35a0dc02d..29de4d231f16667c8297bcbe403f683c30a28762 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,84 @@ struct tdesc_feature : tdesc_element
>  
>  typedef std::unique_ptr<tdesc_feature> tdesc_feature_up;
>  
> +/* The device information in a target description.  */
> +
> +struct tdesc_device : tdesc_element
> +{
> +  tdesc_device ()
> +  {
> +    family = "-";
> +    model = "-";
> +    name = "-";
> +    pci_slot = "-";
> +    uuid = "-";

Why aren't the strings left empty?

> +  }

-- 
Thiago

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [PATCH v2 08/47] gdb, intelgt: add disassemble feature for the Intel GT architecture.
  2024-12-13 15:59 ` [PATCH v2 08/47] gdb, intelgt: add disassemble feature for the Intel GT architecture Tankut Baris Aktemur
@ 2025-07-09  3:12   ` Thiago Jung Bauermann
  0 siblings, 0 replies; 87+ messages in thread
From: Thiago Jung Bauermann @ 2025-07-09  3:12 UTC (permalink / raw)
  To: Tankut Baris Aktemur; +Cc: gdb-patches, Markus Metzger

Tankut Baris Aktemur <tankut.baris.aktemur@intel.com> writes:

> 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.

There should be information about this optional dependency in the GDB
documentation.

It should be in the "Requirements for Building GDB" section of the
manual, and/or (not sure which) in a new "Intel GPU" section under
"Configuration-Specific Information".

For example, one important piece of information that is missing is a
link to where one can get the Intel Gen Assembler library.

> diff --git a/gdb/intelgt-tdep.c b/gdb/intelgt-tdep.c
> index 57c359bf355c5771db38b8d213f6681a043c2b33..7d0ae38480fe89b145c1c29be0e58c96480e3335 100755
> --- 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;
> @@ -119,6 +123,11 @@ struct intelgt_gdbarch_tdep : gdbarch_tdep_base
>      gdb_assert (regset_ranges[intelgt::REGSET_GRF].end > 1);
>      return regset_ranges[intelgt::REGSET_GRF].end - 1;
>    }
> +
> +#if defined (HAVE_LIBIGA64)
> +  /* libiga context for disassembly.  */
> +  iga_context_t iga_ctx = nullptr;
> +#endif
>  };
>  
>  /* The 'register_type' gdbarch method.  */
> @@ -511,13 +520,77 @@ 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_pc_function (addr + (uintptr_t) info->private_data);
> +  return sym ? sym->linkage_name () : nullptr;
> +}

As I mentioned in my review of patch 6, I think you could put the
definition of get_xe_version inside an #if/#endif such as this one.

> +#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.  */
> +  unsigned int full_length = intelgt::inst_length_full ();
> +  unsigned int compact_length = intelgt::inst_length_compacted ();
> +
> +  std::unique_ptr<bfd_byte[]> insn (new bfd_byte[full_length]);
> +
> +  int status = (*info->read_memory_func) (memaddr, insn.get (),
> +					  compact_length, info);
> +  if (status != 0)
> +    {
> +      /* Aborts disassembling with a memory_error exception.  */
> +      (*info->memory_error_func) (status, memaddr, info);
> +      return -1;
> +    }
> +  if (!intelgt::is_compacted_inst (gdb::make_array_view (insn.get (),
> +							 compact_length)))
> +    {
> +      status = (*info->read_memory_func) (memaddr, insn.get (),
> +					  full_length, info);
> +      if (status != 0)
> +	{
> +	  /* Aborts disassembling with a memory_error exception.  */
> +	  (*info->memory_error_func) (status, memaddr, info);
> +	  return -1;
> +	}
> +    }

If GDB wasn't compiled with libiga64, then the only thing that this
function can do is print an error message and return -1, as is done in
the #else block below.

Therefore, all the code above should be inside the #if below.

> +#if defined (HAVE_LIBIGA64)
> +  char *dbuf;
> +  iga_disassemble_options_t dopts = IGA_DISASSEMBLE_OPTIONS_INIT ();
> +  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;
> +  iga_status_t iga_status
> +    = iga_context_disassemble_instruction (iga_ctx, &dopts, insn.get (),
> +					   intelgt_disasm_sym_cb,
> +					   info, &dbuf);
> +  if (iga_status != IGA_SUCCESS)
> +    return -1;
> +
> +  (*info->fprintf_func) (info->stream, "%s", dbuf);
> +
> +  if (intelgt::is_compacted_inst (gdb::make_array_view (insn.get (),
> +							full_length)))
> +    return compact_length;
> +  else
> +    return full_length;
> +#else
> +  gdb_printf (_("\nDisassemble feature not available: libiga64 "
> +		"is missing.\n"));

Is the '\n' at the beginning of the message needed?

>    return -1;
> +#endif /* defined (HAVE_LIBIGA64)  */
>  }
>  
>  /* Utility function to look up the pseudo-register number by name.  Exact
> @@ -864,6 +937,46 @@ 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."));
> +	  gdbarch_free (gdbarch);
> +	  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);
> +	  gdbarch_free (gdbarch);
> +	  return nullptr;
> +	}
> +      else
> +	{
> +	  iga_version = (iga_gen_t) 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);
> +  iga_context_create (&options, &data->iga_ctx);
> +#endif
> +
>    /* Initialize register info.  */
>    set_gdbarch_num_regs (gdbarch, 0);
>    set_gdbarch_register_name (gdbarch, tdesc_register_name);
> diff --git a/gdb/top.c b/gdb/top.c
> index d750f3305f2eef7c5a30cef7f647537916a80499..c27f7d60254a4d384dfd770ce98eb4f0923dee49 100644
> --- a/gdb/top.c
> +++ b/gdb/top.c
> @@ -1523,6 +1523,16 @@ This GDB was configured as follows:\n\
>  "));
>  #endif
>  
> +#ifdef HAVE_LIBIGA64
> +  gdb_printf (stream, _("\
> +	     --with-libiga64\n\
> +"));

This patch adds a "--with-libiga64-prefix=..." configure option, right?
So this should print "--with-libiga64-prefix=...".

> +#else
> +  gdb_printf (stream, _("\
> +	     --without-libiga64\n\
> +"));
> +#endif

-- 
Thiago

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [PATCH v2 09/47] gdbsupport, filestuff, ze: temporary files
  2024-12-13 15:59 ` [PATCH v2 09/47] gdbsupport, filestuff, ze: temporary files Tankut Baris Aktemur
@ 2025-07-14  1:26   ` Thiago Jung Bauermann
  0 siblings, 0 replies; 87+ messages in thread
From: Thiago Jung Bauermann @ 2025-07-14  1:26 UTC (permalink / raw)
  To: Tankut Baris Aktemur; +Cc: gdb-patches, Markus Metzger

Tankut Baris Aktemur <tankut.baris.aktemur@intel.com> writes:

> @@ -544,3 +547,53 @@ read_text_file_to_string (const char *path)
>  
>    return read_remainder_of_file (file.get ());
>  }
> +
> +/* A class to keep temporary files until GDB exits (normally).  */
> +
> +struct tmpfilekeeper_s
> +{
> +  std::list<std::string> files;
> +
> +  tmpfilekeeper_s () = default;
> +  tmpfilekeeper_s (const tmpfilekeeper_s &) = delete;
> +  tmpfilekeeper_s (tmpfilekeeper_s &&) = delete;
> +
> +  ~tmpfilekeeper_s ()
> +  {
> +    for (const std::string &file : files)
> +      {
> +	const char *cfile = file.c_str ();
> +	int status = unlink (cfile);
> +	if (status != 0)
> +	  warning (_("failed to remove temporary file %s: %s"), cfile,
> +		   safe_strerror (errno));
> +      }
> +  }
> +
> +  tmpfilekeeper_s &operator= (const tmpfilekeeper_s &) = delete;
> +  tmpfilekeeper_s &operator= (tmpfilekeeper_s &&) = delete;
> +};
> +static tmpfilekeeper_s tmpfilekeeper;

There is gdb::unlinker defined in gdbsupport/gdb_unlinker.h, so
tmpfilekeeper can be an std::forward_list<gdb::unlinker> (or some other
container) and you don't need struct tmpfilekeeper_s.

But actually, gdb_create_tmpfile is used only in one place, so there's
no need for tmpfilekeepr. I have a suggestion about this in patch 10.

Also, this patch can be squashed with the next one, IMHO.

> +/* See gdbsupport/filestuff.h.  */
> +
> +gdb_file_up
> +gdb_create_tmpfile (std::string &base, int flags)
> +{
> +  std::string absbase { get_standard_temp_dir () + "/" + base };
> +  gdb::char_vector name { make_temp_filename (absbase) };
> +
> +  scoped_fd fd { gdb_mkostemp_cloexec (name.data (), O_BINARY) };
> +  if (fd.get () == -1)
> +    error (_("failed to create temporary file %s: %s"), name.data (),
> +	   safe_strerror (errno));
> +
> +  gdb_file_up file { fd.to_file ("wb") };
> +  if (file.get () == nullptr)
> +    error (_("failed to open %s: %s"), name.data (), safe_strerror (errno));
> +
> +  base = name.data ();
> +  tmpfilekeeper.files.emplace_back (base);
> +
> +  return file;
> +}
> diff --git a/gdbsupport/filestuff.h b/gdbsupport/filestuff.h
> index e2ee141d46f01bc0888a871830a5559419b63f03..6b7d1ecf9354d21e186c6f6b7aceb3aa591c4465 100644
> --- a/gdbsupport/filestuff.h
> +++ b/gdbsupport/filestuff.h
> @@ -71,6 +71,12 @@ gdb_open_cloexec (const std::string &filename, int flags,
>    return gdb_open_cloexec (filename.c_str (), flags, mode);
>  }
>  
> +/* Create a temporary file that will automatically get deleted when GDB
> +   terminates (normally).  NAME needs to be a template filename and will
> +   be modified to contain the actual template filename on return.  */

s/actual template filename/actual filename/

> +
> +extern gdb_file_up gdb_create_tmpfile (std::string &name, int flags = 0);
> +
>  /* Like 'fopen', but ensures that the returned file descriptor has the
>     close-on-exec flag set.  */

-- 
Thiago

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [PATCH v2 10/47] gdb, gdbserver, ze: in-memory libraries
  2024-12-13 15:59 ` [PATCH v2 10/47] gdb, gdbserver, ze: in-memory libraries Tankut Baris Aktemur
@ 2025-07-14  2:35   ` Thiago Jung Bauermann
  2025-07-31  6:09     ` Metzger, Markus T
  2025-07-16  4:08   ` Thiago Jung Bauermann
  1 sibling, 1 reply; 87+ messages in thread
From: Thiago Jung Bauermann @ 2025-07-14  2:35 UTC (permalink / raw)
  To: Tankut Baris Aktemur; +Cc: gdb-patches, Markus Metzger

Tankut Baris Aktemur <tankut.baris.aktemur@intel.com> writes:

> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index ddd414659fcc554813fc2a12a79e581ad7a188b9..c84a8372c223e724e3042322a8bee07b12423050 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -48026,9 +48026,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
> @@ -48040,6 +48041,10 @@ 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}.
>  
> +@value{GDBN} indicates support for in-memory library elements by
> +supplying the @code{qXfer:libraries:read:in-memory-library+}
> +@samp{qSupported} feature (@pxref{qSupported}).
> +
>  A simple memory map, with one loaded library relocated by a single
>  offset, looks like this:
>  
> @@ -48051,6 +48056,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"/>
> +  </library>

Typo: the end tag doesn't match the start tag.

> +</library-list>
> +@end smallexample
> +
>  Another simple memory map, with one loaded library with three
>  allocated sections (.text, .data, .bss), looks like this:
>  
> @@ -48068,14 +48083,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.1">
> +<!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 98ed7bc28dcc4562ef4259fdd78138fe69d69d29..f55071c8e906f091752e8ca78ec29bcd76028433 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)*>
> -<!ATTLIST library-list  version CDATA   #FIXED  "1.0">
> +<!ELEMENT library-list  (library | in-memory-library)*>
> +<!ATTLIST library-list  version CDATA   #FIXED  "1.1">

Theoretically, this would cause the parser to reject an XML unless it
has version="1.1", despite the fact that GDB is still able to parse 1.0
documents.  Therefore, this should just be changed to CDATA #REQUIRED,
and we can rely on library_list_start_list to check if the version is
acceptable.

I said "theoretically" and "would" above because I experimented with it
(actually, with <library-list-svr4> which is what I can test on Linux)
and GDB didn't reject an XML reply with a tag that's not present in the
DTD, and also it didn't care about the value of the version
attribute. The DTD is simply ignored.

Looking around a bit, I found a comment¹ by an Expat maintainer
saying:

> The thing is: Expat is a non-validating XML parser.

So I'm not sure what purpose the DTDs have in GDB. Perhaps they're just
part of the documentation?

If the DTD was enforced, it would be ok to keep version at 1.0 since an
old GDB would reject a library-list document with in-memory-library
elements because it wouldn't conform to the expected DTD. As it is
though, it looks like the version bump is indeed necessary.

There's another version bump elsewhere in this series though, which I
think isn't really needed in practice — will there ever be a version of
GDB in the field which supports version 1.1 but not 1.2?

>  <!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>
>

<snip>

> @@ -246,10 +295,35 @@ solib_target_current_sos (void)
>    for (lm_info_target_up &info : library_list)
>      {
>        auto &new_solib = sos.emplace_back ();
> +      switch (info->location)
> +	{
> +	case lm_on_disk:
> +	  /* We don't need a copy of the name in INFO anymore.  */
> +	  new_solib.so_name = std::move (info->name);
> +	  new_solib.so_original_name = new_solib.so_name;
> +	  break;
> +
> +	case lm_in_memory:
> +	  {
> +	    if (info->end <= info->begin)
> +	      error (_("bad in-memory-library location: begin=%s, end=%s"),
> +		     core_addr_to_string_nz (info->begin),
> +		     core_addr_to_string_nz (info->end));

Is erroring out better than printing a warning and skipping the bad
library entry?

> +	    /* Give it a name although this isn't really needed.  */
> +	    std::string orig_name
> +	      = std::string ("in-memory-")
> +	      + core_addr_to_string_nz (info->begin)
> +	      + "-"
> +	      + core_addr_to_string_nz (info->end);
> +
> +	    new_solib.so_original_name = orig_name;

I know less about C++ than I would like: would it be better to use
std::move (orig_name) here, or can we rely on the compiler being smart
enough to notice that it doesn't need orig_name after this line and
avoid a copy? If you don't know either, I'd add an std::move just to be
sure.

> +	    new_solib.begin = info->begin;
> +	    new_solib.end = info->end;
> +	  }
> +	  break;
> +	}
>  
> -      /* We don't need a copy of the name in INFO anymore.  */
> -      new_solib.so_name = std::move (info->name);
> -      new_solib.so_original_name = new_solib.so_name;
>        new_solib.lm_info = std::move (info);
>      }

<snip>

> diff --git a/gdb/solist.h b/gdb/solist.h
> index 8ad0aefd9e32b264b08a853eb3577829a8e74767..c4d053a5c6c0261f1176121b2bc1f904ac2424a4 100644
> --- a/gdb/solist.h
> +++ b/gdb/solist.h
> @@ -67,9 +67,14 @@ struct solib : intrusive_list_node<solib>
>       map we've already loaded.  */
>    std::string so_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 so_name;
>  
> +  /* The address range of an in-memory shared object.  Both BEGIN and END
> +     are zero for on-disk shared objects.  */
> +  CORE_ADDR begin, end;

It's better to initialize these to 0 here.

>    /* 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.
> @@ -180,6 +185,12 @@ struct solib_ops
>       name).  */
>  
>    std::optional<CORE_ADDR> (*find_solib_addr) (solib &so);
> +
> +  /* Open an in-memory shared library at ADDR of at most SIZE bytes.  The
> +     TARGET string is used to identify the target.  */
> +  gdb_bfd_ref_ptr (*bfd_open_from_target_memory) (CORE_ADDR addr,
> +						  CORE_ADDR size,
> +						  const char *target);
>  };
>  
>  /* A unique pointer to a so_list.  */
> diff --git a/gdbserver/dll.cc b/gdbserver/dll.cc
> index f49aa560aec57e246948d944ec377b0beb8d1dc5..c2c63e9a69a54ddede77e40ea33b4e19cbdc71af 100644
> --- a/gdbserver/dll.cc
> +++ b/gdbserver/dll.cc
> @@ -40,6 +40,17 @@ loaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
>    proc->dlls_changed = true;
>  }
>  
> +/* Record a newly loaded in-memory DLL at BASE_ADDR for PROC.  */

You should also document the BEGIN and END arguments. In particular, I'm
interested in learning the difference between BEGIN and BASE_ADDR.

> +
> +void
> +loaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
> +	    CORE_ADDR base_addr)
> +{
> +  gdb_assert (proc != nullptr);
> +  proc->all_dlls.emplace_back (begin, end, base_addr);
> +  proc->dlls_changed = true;
> +}

Even though both versions of loaded_dll are small, it's a bit
unfortunate that they're almost identical. This is because of the choice
to have two constructors for dll_info and the location member being
initialized depending on which one is called. I understand that design
choice, but given that it leads to this duplication of code I think it's
better to have location explicit in the constructor (or a new
constructor with location explicitly passed in it) and then one version
of loaded_dll can simply call the other one (or both can call a third,
internal loaded_dll version) with an explicit location argument.

>  /* Record that the DLL with NAME and BASE_ADDR has been unloaded
>     from the current process.  */
>  
> @@ -58,6 +69,9 @@ unloaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
>    gdb_assert (proc != nullptr);
>    auto pred = [&] (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;
> @@ -89,3 +103,48 @@ unloaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
>        proc->dlls_changed = true;
>      }
>  }
> +
> +/* 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)
> +{
> +  gdb_assert (proc != nullptr);
> +  auto pred = [&] (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 != UNSPECIFIED_CORE_ADDR
> +	  && begin == dll.begin
> +	  && (end == UNSPECIFIED_CORE_ADDR
> +	      || end == dll.end))
> +	return true;
> +
> +      return false;
> +    };
> +
> +  auto iter = std::find_if (proc->all_dlls.begin (), proc->all_dlls.end (),
> +			    pred);
> +
> +  if (iter == proc->all_dlls.end ())
> +    /* For some inferiors we might get unloaded_dll events without having
> +       a corresponding loaded_dll.  In that case, the dll cannot be found
> +       in ALL_DLL, and there is nothing further for us to do.  */
> +    return;
> +  else
> +    {
> +      /* DLL has been found so remove the entry and free associated
> +	 resources.  */
> +      proc->all_dlls.erase (iter);
> +      proc->dlls_changed = 1;
> +    }
> +}

Here the duplication problem between the two versions of unloaded_dll is
worse. It's possible do define an unloaded_dll_1 internal function with
the code above and getting an dll_info::location_t as argument that is
called by both versions of unloaded_dll.

> diff --git a/gdbserver/dll.h b/gdbserver/dll.h
> index e603df4be2b26017fa5b795f861d8d4111779d85..9bce28e70cf3939b5b062e4b9c0f0d2d10a1d1d7 100644
> --- a/gdbserver/dll.h
> +++ b/gdbserver/dll.h
> @@ -24,19 +24,36 @@ 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_)
>    {}
>  
> +  location_t location;
>    std::string name;
> +  CORE_ADDR begin;

Here too it would be nice to have a comment explaining the difference
between begin and base_addr.

> +  CORE_ADDR end;
>    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 264fac44b74db69746fdecb1cdd1fd748139cc4f..90902099a1b5a84fe86cb8391bf983a5943e78c5 100644
> --- a/gdbserver/server.cc
> +++ b/gdbserver/server.cc
> @@ -1884,6 +1884,97 @@ handle_qxfer_features (const char *annex,
>    return len;
>  }
>  
> +static std::string
> +dll_to_tmpfile (dll_info &dll)

There should be a documentation comment for this function.

> +{
> +  if (dll.end <= dll.begin)
> +    error (_("bad in-memory-library location: begin=%s, end=%s"),
> +	   core_addr_to_string_nz (dll.begin),
> +	   core_addr_to_string_nz (dll.end));
> +
> +  gdb::byte_vector buffer (dll.end - dll.begin);
> +  int errcode = gdb_read_memory (dll.begin, buffer.data (), buffer.size ());
> +  if (errcode != 0)
> +    error (_("failed to read in-memory library at %s..%s"),
> +	   core_addr_to_string_nz (dll.begin),
> +	   core_addr_to_string_nz (dll.end));
> +
> +  std::string name
> +    = std::string ("gdb-in-memory-solib-")
> +    + core_addr_to_string_nz (dll.begin)
> +    + "-"
> +    + core_addr_to_string_nz (dll.end);
> +
> +  gdb_file_up file = gdb_create_tmpfile (name);

This is the only use of gdb_create_tmpfile. I think that instead of
having files that will be deleted when gdbserver exits, it would be
better if the files were deleted when they aren't necessary anymore.

gdb_create_tmpfile could return not only a gdb_file_up, but also a
gdb::unlinker (in an std::pair, I suppose). Then dll_to_tmpfile could
add it to new member of dll_info with type
std::optional<gdb::unlinker>. Thus, when the dll_info object is
destroyed, the tmp file will be deleted. WDYT?

Also, this could be a dll_info::to_tmpfile method instead of a separate
function.

> +  size_t written = fwrite (buffer.data (), buffer.size (), 1, file.get ());
> +  if (written != 1)
> +    error (_("failed to write into %s"), name.c_str ());
> +
> +  return name;
> +}
> +
> +/* Print a qXfer:libraries:read entry for DLL.  */
> +
> +static std::string
> +print_qxfer_libraries_entry (dll_info &dll)
> +{
> +  switch (dll.location)
> +    {
> +    case dll_info::in_memory:
> +      if (get_client_state ().in_memory_library_supported)
> +	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));
> +
> +      /* GDB does not support in-memory-library.  Fall back to storing it in a
> +	 temporary file and report that file to GDB.  */
> +      dll.name = dll_to_tmpfile (dll);
> +      [[fallthrough]];
> +
> +    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));
> +    }
> +
> +  warning (_("unknown dll location: %x"), dll.location);

This can only happen via some internal error in gdbserver, right?
Isn't it better to use gdb_assert_not_reached?

> +  return std::string ();
> +}
> +
> +/* Determine the library-list version required for communicating the shared
> +   libraries.  */
> +
> +static std::string
> +library_list_version_needed (const std::list<dll_info> &dlls)
> +{
> +  const client_state &cs = get_client_state ();
> +  int major = 1, minor = 0;
> +
> +  for (const dll_info &dll : dlls)
> +    {
> +      switch (dll.location)
> +	{
> +	case dll_info::on_disk:
> +	  major = std::max (major, 1);
> +	  minor = std::max (minor, 0);
> +	  break;
> +
> +	case dll_info::in_memory:
> +	  if (cs.in_memory_library_supported)
> +	    {
> +	      major = std::max (major, 1);
> +	      minor = std::max (minor, 1);
> +	    }
> +	  break;
> +	}
> +    }
> +
> +  return std::to_string (major) + std::string (".") + std::to_string (minor);
> +}

This makes the version depend on what the currnet list of dll_infos
needs, not just on the features supported by GDB or gdbserver. If
there's no in-memory library loaded, then the XML will be version
1.0. If later an in-memory library is loaded, then the next XML will be
version 1.1

I expected the version to depend just on the features advertised by GDB
and gdbserver, so I was a bit surprised. I don't think this is a
problem, but I just mention here in case someone has an opinion about
it.

<snip>

> diff --git a/gdbserver/server.h b/gdbserver/server.h
> index e1297d41f8406dab670397f5600e4e08558f23b7..3bacd1062f8bf47720189d2d02b36ddb6089e6bc 100644
> --- a/gdbserver/server.h
> +++ b/gdbserver/server.h
> @@ -168,6 +168,9 @@ struct client_state
>       space randomization feature before starting an inferior.  */
>    int disable_randomization = 1;
>  
> +  /* True if qXfer:libraries:read supports in-memory-library.  */
> +  bool in_memory_library_supported = false;
> +

Nit: I would add this to the end of the struct, together with the other
bools about protocol features.

>    int pass_signals[GDB_SIGNAL_LAST];
>    int program_signals[GDB_SIGNAL_LAST];
>    int program_signals_p = 0;

-- 
Thiago

¹ https://github.com/libexpat/libexpat/issues/968#issuecomment-2692251715

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [PATCH v2 10/47] gdb, gdbserver, ze: in-memory libraries
  2024-12-13 15:59 ` [PATCH v2 10/47] gdb, gdbserver, ze: in-memory libraries Tankut Baris Aktemur
  2025-07-14  2:35   ` Thiago Jung Bauermann
@ 2025-07-16  4:08   ` Thiago Jung Bauermann
  1 sibling, 0 replies; 87+ messages in thread
From: Thiago Jung Bauermann @ 2025-07-16  4:08 UTC (permalink / raw)
  To: Tankut Baris Aktemur; +Cc: gdb-patches, Markus Metzger

Hello again,

I just noticed a couple of things I missed in my review:

Tankut Baris Aktemur <tankut.baris.aktemur@intel.com> writes:

> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index ddd414659fcc554813fc2a12a79e581ad7a188b9..c84a8372c223e724e3042322a8bee07b12423050 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -48026,9 +48026,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
> @@ -48040,6 +48041,10 @@ 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}.
>  
> +@value{GDBN} indicates support for in-memory library elements by
> +supplying the @code{qXfer:libraries:read:in-memory-library+}
> +@samp{qSupported} feature (@pxref{qSupported}).

This feature should be added to the list in the qSupported documentation
entry.

>  A simple memory map, with one loaded library relocated by a single
>  offset, looks like this:

<snip>

> diff --git a/gdb/remote.c b/gdb/remote.c
> index f41129915683194237d1bba56d17df61ae89c063..7d074a5df322d68ded8f96c4832bc8c247435a4f 100644
> --- a/gdb/remote.c
> +++ b/gdb/remote.c
> @@ -5956,6 +5956,9 @@ remote_target::remote_query_supported ()
>  	  != AUTO_BOOLEAN_FALSE)
>  	remote_query_supported_append (&q, "memory-tagging+");
>  
> +      remote_query_supported_append
> +	(&q, "qXfer:libraries:read:in-memory-library+");

This is the only feature in this function that isn't guarded by a "set
remote foo-packet" command state. Shouldn't it?

>        /* Keep this one last to work around a gdbserver <= 7.10 bug in
>  	 the qSupported:xmlRegisters=i386 handling.  */
>        if (remote_support_xml != NULL

-- 
Thiago

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [PATCH v2 11/47] gdb, gdbserver, rsp, ze: acknowledge libraries
  2024-12-13 15:59 ` [PATCH v2 11/47] gdb, gdbserver, rsp, ze: acknowledge libraries Tankut Baris Aktemur
  2024-12-13 16:43   ` Eli Zaretskii
@ 2025-07-16  4:20   ` Thiago Jung Bauermann
  2025-07-31  6:09     ` Metzger, Markus T
  1 sibling, 1 reply; 87+ messages in thread
From: Thiago Jung Bauermann @ 2025-07-16  4:20 UTC (permalink / raw)
  To: Tankut Baris Aktemur; +Cc: gdb-patches, Markus Metzger

Hello Baris,

Tankut Baris Aktemur <tankut.baris.aktemur@intel.com> writes:

> @@ -48084,16 +48141,18 @@ The format of a library list is described by this DTD:
>  @smallexample
>  <!-- library-list: Root element with versioning -->
>  <!ELEMENT library-list            (library | in-memory-library)*>
> -<!ATTLIST library-list            version CDATA   #FIXED  "1.1">
> +<!ATTLIST library-list            version CDATA        #FIXED  "1.2">
>  <!ELEMENT library                 (segment*, section*)>
> -<!ATTLIST library                 name    CDATA   #REQUIRED>
> +<!ATTLIST library                 name    CDATA        #REQUIRED
> +                                  ack     (yes | no)   'no'>
>  <!ELEMENT in-memory-library       (segment*, section*)>
> -<!ATTLIST in-memory-library       begin    CDATA   #REQUIRED
> -                                  end      CDATA   #REQUIRED>
> +<!ATTLIST in-memory-library       begin    CDATA       #REQUIRED
> +                                  end      CDATA       #REQUIRED
> +                                  ack      (yes | no)  'no'>
>  <!ELEMENT segment                 EMPTY>
> -<!ATTLIST segment                 address CDATA   #REQUIRED>
> +<!ATTLIST segment                 address CDATA        #REQUIRED>
>  <!ELEMENT section                 EMPTY>
> -<!ATTLIST section                 address CDATA   #REQUIRED>
> +<!ATTLIST section                 address CDATA        #REQUIRED>
>  @end smallexample

I would move the last column (the one with "#FIXED", "#REQUIRED" and
'no') a couple more spaces to the right. The change above leaves them
right next to "(segment*, section*)>" and it feels a bit crowded IMHO.

>  
>  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 f55071c8e906f091752e8ca78ec29bcd76028433..473b8eaa719b1d310f9eb4fe8f531df85008845d 100644
> --- a/gdb/features/library-list.dtd
> +++ b/gdb/features/library-list.dtd
> @@ -6,14 +6,16 @@
>  
>  <!-- library-list: Root element with versioning -->
>  <!ELEMENT library-list  (library | in-memory-library)*>
> -<!ATTLIST library-list  version CDATA   #FIXED  "1.1">
> +<!ATTLIST library-list  version CDATA   #FIXED  "1.2">

Same comment here as in patch 10: IMO it's better to just use CDATA
#REQUIRED and rely on library_list_start_list to check if the version is
acceptable.

Also, same comment about bumping the version only once in the series, to
1.1.

>  
>  <!ELEMENT library       (segment*, section*)>
> -<!ATTLIST library       name    CDATA   #REQUIRED>
> +<!ATTLIST library       name    CDATA        #REQUIRED
> +                        ack     (yes | no)   'no'>
>  
>  <!ELEMENT in-memory-library       (segment*, section*)>
> -<!ATTLIST in-memory-library       begin    CDATA   #REQUIRED
> -                                  end      CDATA   #REQUIRED>
> +<!ATTLIST in-memory-library       begin    CDATA       #REQUIRED
> +                                  end      CDATA       #REQUIRED
> +                                  ack      (yes | no)  'no'>
>  
>  <!ELEMENT segment       EMPTY>
>  <!ATTLIST segment       address CDATA   #REQUIRED>

Same comment here about moving the last column a couple of spaces to the
right.

<snip>

> @@ -15679,6 +15700,60 @@ remote_target::vcont_r_supported ()
>  	  && get_remote_state ()->supports_vCont.r);
>  }
>  
> +void
> +remote_target::ack_library (const char *name)

Missing doc comment for this method.

> +{
> +  struct remote_state *rs = get_remote_state ();

No need for the struct keyword.

> +  char *p = rs->buf.data ();
> +  char *endp = p + get_remote_packet_size ();
> +
> +  xsnprintf (p, endp - p, "vAck:library:%s", name);
> +
> +  putpkt (rs->buf);
> +  getpkt (&rs->buf, 0);
> +
> +  packet_result result
> +    = m_features.packet_ok (rs->buf, PACKET_vAck_library);

This fits 80 columns as one line.

> +  switch (result.status ())
> +    {
> +    case PACKET_OK:
> +      break;
> +    case PACKET_UNKNOWN:
> +      error (_("No support for acknowledging libraries."));
> +    case PACKET_ERROR:
> +      error (_("Acknowledging library '%s' failed: '%s'"), name,
> +	     rs->buf.data ());
> +    }
> +}
> +
> +void
> +remote_target::ack_in_memory_library (CORE_ADDR begin, CORE_ADDR end)

Missing doc comment for this method.

> +{
> +  struct remote_state *rs = get_remote_state ();

No need for the struct keyword.

> +  char *p = rs->buf.data ();
> +  char *endp = p + get_remote_packet_size ();
> +
> +  xsnprintf (p, endp - p, "vAck:in-memory-library:%s,%s",
> +	     core_addr_to_string_nz (begin), core_addr_to_string_nz (end));
> +
> +  putpkt (rs->buf);
> +  getpkt (&rs->buf, 0);
> +
> +  packet_result result
> +    = m_features.packet_ok (rs->buf, PACKET_vAck_in_memory_library);
> +  switch (result.status ())
> +    {
> +    case PACKET_OK:
> +      break;
> +    case PACKET_UNKNOWN:
> +      error (_("No support for acknowledging in-memory libraries."));
> +    case PACKET_ERROR:
> +      error (_("Failed to acknowledge in-memory library %s-%s: %s"),
> +	     core_addr_to_string_nz (begin), core_addr_to_string_nz (end),
> +	     rs->buf.data ());
> +    }
> +}
> +
>  /* The "set/show range-stepping" set hook.  */
>  
>  static void
> @@ -16442,6 +16517,12 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
>  
>    add_packet_config_cmd (PACKET_vCtrlC, "vCtrlC", "ctrl-c", 0);
>  
> +  add_packet_config_cmd (PACKET_vAck_library,
> +			 "vAck:library", "ack-library", 0);

This fits 80 columns as one line.

> +
> +  add_packet_config_cmd (PACKET_vAck_in_memory_library,
> +			 "vAck:in-memory-library", "ack-in-memory-library", 0);
> +
>    add_packet_config_cmd (PACKET_QThreadEvents, "QThreadEvents", "thread-events",
>  			 0);

<snip>

> @@ -473,6 +503,32 @@ solib_target_in_dynsym_resolve_code (CORE_ADDR pc)
>    return in_plt_section (pc);
>  }
>  
> +static void
> +solib_target_ack_library (solib &so)
> +{
> +  lm_info_target *lm
> +    = gdb::checked_static_cast<lm_info_target *> (so.lm_info.get ());
> +
> +  if (!lm->need_ack)
> +    return;
> +
> +  /* Try only once, whether we succeed or not.  */
> +  lm->need_ack = false;
> +  switch (lm->location)
> +    {
> +    case lm_on_disk:
> +      target_ack_library (so.so_original_name.c_str ());
> +      return;
> +
> +    case lm_in_memory:
> +      target_ack_in_memory_library (lm->begin, lm->end);
> +      return;
> +    }
> +
> +  warning (_("bad solib location '%d' for %s."), lm->location,
> +	   so.so_original_name.c_str ());

Isn't this an internal error? If so, it should call
gdb_assert_not_reached.

> +}
> +
>  const solib_ops solib_target_so_ops =
>  {
>    solib_target_relocate_section_addresses,
> @@ -489,4 +545,5 @@ const solib_ops solib_target_so_ops =
>    nullptr,
>    default_find_solib_addr,
>    gdb_bfd_open_from_target_memory,
> +  solib_target_ack_library,
>  };
> diff --git a/gdb/solib.c b/gdb/solib.c
> index cb302f9215257ddff18c94f5be39e189b02d84e6..92fc5137a3d469f55043235a65dcadcc1f25fe96 100644
> --- a/gdb/solib.c
> +++ b/gdb/solib.c
> @@ -967,6 +967,7 @@ solib_add (const char *pattern, int from_tty, int readsyms)
>      if (from_tty)
>        add_flags |= SYMFILE_VERBOSE;
>  
> +    std::list<solib *> added_solibs;
>      for (solib &gdb : current_program_space->solibs ())
>        if (!pattern || re_exec (gdb.so_name.c_str ()))
>  	{
> @@ -989,14 +990,23 @@ solib_add (const char *pattern, int from_tty, int readsyms)
>  				styled_string (file_name_style.style (),
>  					       gdb.so_name.c_str ()));
>  		}
> -	      else if (solib_read_symbols (gdb, add_flags))
> -		loaded_any_symbols = true;
> +	      else
> +		added_solibs.emplace_back (&gdb);

This should use the push_back method since we don't want to create a new
object in the list.

>  	    }
>  	}
>  
> +    for (solib *gdb : added_solibs)
> +      if (solib_read_symbols (*gdb, add_flags))
> +	loaded_any_symbols = true;
> +
>      if (loaded_any_symbols)
>        breakpoint_re_set ();
>  
> +    /* Acknowledge loading of new solibs.  This must be called after
> +       breakpoints have been set in this newly loaded solib.  */
> +    for (solib *gdb : added_solibs)
> +      solib_ack_library (*gdb);
> +
>      if (from_tty && pattern && !any_matches)
>        gdb_printf ("No loaded shared libraries match the pattern `%s'.\n",
>  		  pattern);
> @@ -1700,6 +1710,16 @@ default_find_solib_addr (solib &so)
>    return {};
>  }
>  
> +/* See solist.h.  */
> +
> +void solib_ack_library (solib &so)

This function is only called from solib_add, shouldn't it be static?
Or even just inlined into the caller?

> +{
> +  const solib_ops *ops = gdbarch_so_ops (current_inferior ()->arch ());
> +
> +  if (ops->ack_library != nullptr)
> +    (*ops->ack_library) (so);
> +}
> +
>  void _initialize_solib ();
>  
>  void

<snip>

> @@ -60,6 +72,78 @@ unloaded_dll (const char *name, CORE_ADDR base_addr)
>    unloaded_dll (current_process (), name, base_addr);
>  }
>  
> +static void
> +ack_dll (process_info *process, dll_info &dll)
> +{
> +  gdb_assert (dll.need_ack);
> +
> +  switch (dll.location)
> +    {
> +    case dll_info::on_disk:
> +      /* Check if this is a temporary file for an in-memory library.  */
> +      if (dll.begin == UNSPECIFIED_CORE_ADDR)
> +	{
> +	  target_ack_library (process, dll.name.c_str ());
> +	  dll.need_ack = false;
> +	  return;
> +	}
> +
> +      [[fallthrough]];
> +    case dll_info::in_memory:
> +      target_ack_in_memory_library (process, dll.begin, dll.end);
> +      dll.need_ack = false;
> +      return;
> +    }

I'm confused by this switch statement. So a vAck:library packet will be
used only if it's for an in-memory library and vAck:in-memory-library
isn't supported? I would think that a vAck:library would also be used
for an on-disk library.

> +  internal_error (_("bad library location: %d."), dll.location);
> +}
> +
> +void
> +ack_dll (process_info *proc, const char *name)
> +{
> +  std::list<dll_info> &dlls = proc->all_dlls;
> +  std::list<dll_info>::iterator it
> +    = std::find_if (dlls.begin (), dlls.end (),
> +		    [name] (const dll_info &dll)
> +	{
> +	  return (dll.name == std::string (name));
> +	});
> +
> +  if (it != dlls.end ())
> +    ack_dll (proc, *it);
> +}
> +
> +void
> +ack_dll (const char *name)
> +{
> +  ack_dll (current_process (), name);
> +}
> +
> +void
> +ack_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end)
> +{
> +  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)
> +	{
> +	  /* For root devices with multiple sub-devices, modules with
> +	     identical start/end addresses may be received for different
> +	     sub-devices.  Therefore we check for the 'NEED_ACK' flag in
> +	     the search, too.  */

And is it guaranteed that only one of them will have need_ack set to
true? Can you explain that in the comment?

> +	  return ((dll.begin == begin) && (dll.end == end) && dll.need_ack);
> +	});
> +
> +  if (it != dlls.end ())
> +    ack_dll (proc, *it);
> +}

<snip>

> @@ -3371,6 +3398,66 @@ err:
>    return;
>  }
>  
> +/* Parse vAck packets.  */
> +
> +static void
> +handle_v_ack (char *own_buf)
> +{
> +  client_state &cs = get_client_state ();
> +  char *p;
> +
> +  /* Move past vAck: to the first type string.  */
> +  p = &own_buf[5];
> +  do
> +    {
> +      if (cs.vack_library_supported
> +	  && (strncmp (p, "library:", strlen ("library:")) == 0))
> +	{
> +	  p += strlen ("library:");
> +
> +	  /* We expect a single argument: the filename.  */
> +	  const char *name = p;
> +	  p = strchr (p, ';');
> +	  if (p != nullptr)
> +	    *p++ = '\0';
> +
> +	  ack_dll (name);

The documentation for vAck says:

  Acknowledge a ‘;’-separated list of remote stub responses, each with a
  ‘,’- separated list of arguments defined by its type.

But the above will consider everything between the ':' and ';' to be the
library name, even if there's a ','. I think you should check whether
there's a ',' in name, and if so either reject the packet or ignore the
additional arguments — not sure which is better, I'm leaning towards
rejecting the packet.

> +	}
> +      else if (cs.vack_in_memory_library_supported
> +	       && (strncmp (p, "in-memory-library:",
> +			    strlen ("in-memory-library:")) == 0))
> +	{
> +	  p += strlen ("in-memory-library:");
> +
> +	  /* We expect two arguments: begin and end address.  */
> +	  CORE_ADDR begin, end;
> +
> +	  begin = (CORE_ADDR) strtoull (p, &p, 16);
> +	  if (*p != ',')
> +	    break;

If this break is taken and begin is the last argument in the packet,
then won't *p == 0 and cause write_ok () to be called below? An error
should be returned instead.

> +	  end = (CORE_ADDR) strtoull (p+1, &p, 16);
> +	  if (*p == ';')
> +	    p += 1;
> +	  else if (*p != 0)
> +	    break;
> +
> +	  ack_dll (begin, end);
> +	}
> +      else
> +	break;
> +    }
> +  while (p != nullptr && *p != 0);
> +
> +  if (p == nullptr || *p == 0)
> +    write_ok (own_buf);
> +  else
> +    {
> +      std::string junk { p };
> +      sprintf (own_buf, "E.junk in vAck: '%s'.", junk.c_str ());
> +    }
> +}

-- 
Thiago

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [PATCH v2 13/47] gdb, remote, ze: fix "$Hc-1#09...Packet received: E01" during startup
  2024-12-13 15:59 ` [PATCH v2 13/47] gdb, remote, ze: fix "$Hc-1#09...Packet received: E01" during startup Tankut Baris Aktemur
@ 2025-07-18  0:41   ` Thiago Jung Bauermann
  2025-08-01  7:55     ` Metzger, Markus T
  0 siblings, 1 reply; 87+ messages in thread
From: Thiago Jung Bauermann @ 2025-07-18  0:41 UTC (permalink / raw)
  To: Tankut Baris Aktemur; +Cc: gdb-patches, Markus Metzger

Hello,

You know you're looking at an interesting patch when the commit message
is one page long, but it changes only one line. :)

Regarding the subject:

Can you expand a bit on what conditions cause this error to happen?  Is
it broken only for the ze target, or do other targets also get the
error?

Is it returned when, during handling of 'H', find_thread_ptid can't find
the "<PID>.-1" thread? I.e., by this code?

  else
    {
      /* The ptid represents a lwp/tid.  */
      if (find_thread_ptid (thread_id) == NULL)
        {
          write_enn (cs.own_buf);
          break;
        }
    }

Tankut Baris Aktemur <tankut.baris.aktemur@intel.com> writes:

> From: Markus Metzger <markus.t.metzger@intel.com>
>
> When opening a connection, the remote target does
>
>     set_continue_thread (minus_one_ptid);

One interesting aspect of this line is the comment above it:

      /* Let the stub know that we want it to return the thread.  */
      set_continue_thread (minus_one_ptid);

What does it "the stub [...] to return the thread" mean? I tried reading
in gdbserver code what it would do if it understood the Hc packet as
referring to null_ptid or minus_one_ptid, but I couldn't find anything
that could be described as "returning the thread".

And yet it appears to be the purpose of the line above. I'd say we need
someone who understands this aspect to review the patch.

> which results in
>
>     $Hc-1#09
>
> to be send to gdbserver.  In remote-utils.c:read_ptid (), this is read as
>
>     { <current inferior>, -1, 0 }

Nit: I'd say "<current PID>" rather than "<current inferior>".

> and not recognized as minus_one_ptid when handling 'H' in
>
> 	  ptid_t thread_id = read_ptid (&cs.own_buf[2], NULL);
>
> 	  if (thread_id == null_ptid || thread_id == minus_one_ptid)
> 	    thread_id = null_ptid;
>
> Since minus_one_ptid and null_ptid are treated the same way, this is
> probably what we want.

Not sure I understood what "this" means in the sentence. If it means "we
want our Hc packet to be interpreted as thread_id == nullptid in the if
condition above", then that's not what this patch does (sorry if I
misunderstood). As you noted, read_ptid can't return a minus_one_ptid,
but it can't return a null_ptid either.

With the patch applied, read_ptid will return { <current pid>, 0, 0 },
which ptid_t::is_pid () considers a PID and thus the handling of 'H'
will call find_any_thread_of_pid, which I'd guess is why this patch
works.

> In remote_target::remote_resume_with_hc (), we do
>
>       if (ptid == minus_one_ptid)
>         set_continue_thread (any_thread_ptid);
>       else
>         set_continue_thread (ptid);

There's no talk of wanting the stub to "return the thread" here though.

> which amounts to the same thing as set_thread () does

Not exactly. In the code below, there's one more "else if" branch:

>       *buf++ = 'H';
>       *buf++ = gen ? 'g' : 'c';
>       if (ptid == magic_null_ptid)
>         xsnprintf (buf, endbuf - buf, "0");
>       else if (ptid == any_thread_ptid)
>         xsnprintf (buf, endbuf - buf, "0");

  else if (ptid == minus_one_ptid)
    xsnprintf (buf, endbuf - buf, "-1");

So remote_target::set_thread doesn't treat any_thread_ptid and
minus_one_ptid the same way, even though gdbserver apparently does.

Also, the doc comment of set_thread says: "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."

Here's the talk about asking the stub to "return the thread"
again. Frustrating.

> so any_thread_ptid is pretty much the same as null_ptid and as
> minus_one_ptid in this context.

gdbserver appears to think so, but GDB doesn't. I'm confused.

> Use any_thread_ptid to align with remote_target::remote_resume_with_hc ().
> ---
>  gdb/remote.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/gdb/remote.c b/gdb/remote.c
> index 281e616eb83008e75da8a18efaee7001ddbf6d0d..3aaa1614211d617e0d7683eade88f0d00e1bee8b 100644
> --- a/gdb/remote.c
> +++ b/gdb/remote.c
> @@ -5284,7 +5284,7 @@ remote_target::start_remote_1 (int from_tty, int extended_p)
>        target_update_thread_list ();
>  
>        /* Let the stub know that we want it to return the thread.  */
> -      set_continue_thread (minus_one_ptid);
> +      set_continue_thread (any_thread_ptid);
>  
>        if (thread_count (this) == 0)
>  	{

-- 
Thiago

^ permalink raw reply	[flat|nested] 87+ messages in thread

* Re: [PATCH v2 12/47] gdb, solib, ze: solib_bfd_open_from_target_memory
  2024-12-13 15:59 ` [PATCH v2 12/47] gdb, solib, ze: solib_bfd_open_from_target_memory Tankut Baris Aktemur
@ 2025-07-18  0:42   ` Thiago Jung Bauermann
  0 siblings, 0 replies; 87+ messages in thread
From: Thiago Jung Bauermann @ 2025-07-18  0:42 UTC (permalink / raw)
  To: Tankut Baris Aktemur; +Cc: gdb-patches, Markus Metzger

Tankut Baris Aktemur <tankut.baris.aktemur@intel.com> writes:

> From: Markus Metzger <markus.t.metzger@intel.com>
>
> Add solib_bfd_open_from_target_memory to mimic the solib_bfd_open
> behavior on top of gdb_bfd_open for in-memory files.
> ---
>  gdb/solib-target.c |  2 +-
>  gdb/solib.c        | 52 ++++++++++++++++++++++++++++++++++++++++------------
>  gdb/solist.h       |  5 +++++
>  3 files changed, 46 insertions(+), 13 deletions(-)

Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>

-- 
Thiago

^ permalink raw reply	[flat|nested] 87+ messages in thread

* RE: [PATCH v2 06/47] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture
  2025-07-08  2:43   ` Thiago Jung Bauermann
@ 2025-07-18 17:43     ` Aktemur, Tankut Baris
  0 siblings, 0 replies; 87+ messages in thread
From: Aktemur, Tankut Baris @ 2025-07-18 17:43 UTC (permalink / raw)
  To: Thiago Jung Bauermann, gdb-patches; +Cc: Metzger, Markus T

Hi Thiago,

On Tuesday, July 8, 2025 4:43 AM, Thiago Jung Bauermann wrote:
> Hello Baris,
> 
> Reviewing this patch series has been on my todo list for a long
> time. I'll try to go through it this week.

Much appreciated.  Thank you.
Below I respond to a number of comments.  For all the other comments, I updated the
code locally and will include the change in the next version.

> > diff --git a/gdb/intelgt-tdep.c b/gdb/intelgt-tdep.c
> > new file mode 100755
> > index 0000000000000000000000000000000000000000..57c359bf355c5771db38b8d213f6681a043c2b33
> > --- /dev/null
> > +++ b/gdb/intelgt-tdep.c
> > @@ -0,0 +1,980 @@
> > +/* Target-dependent code for the Intel(R) Graphics Technology architecture.
> > +
> > +   Copyright (C) 2019-2024 Free Software Foundation, Inc.
> 
> Does the copyright on this file really start on 2019?

Yes, 2019 is correct.

> > +
> > +/* Helper functions to request and translate the device id/version.  */
> > +
> > +[[maybe_unused]] static xe_version get_xe_version (unsigned int device_id);
> 
> If the definition of get_xe_version is guarded by "#ifdef
> HAVE_LIBIGA64", then this prototype isn't needed.
> 
> Also, the definition of get_xe_version can be moved to patch 8 ("gdb,
> intelgt: add disassemble feature for the Intel GT architecture."), which
> uses it.

We moved the definition to arch/intelgt.h and .c.  There are/will be uses in
gdbserver/intelgt-ze-low.cc, too, in the next version after some refactoring.

> > +/* 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);
> 
> There's no need to break the line above. Everything fits in one line.

It goes until column 77, which was beyond the soft limit.  Hence it was broken.
Similarly, for some

  intelgt_gdbarch_tdep *data
    = gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch);

cases for which you commented, the line goes up to column 76 if not broken.
I kept them as is.  For others, I removed the line break.
(This does not mean, however, that the file always follows the soft-limit rule.)

> > +/* 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;
> > +}
> > +
> > +static const struct frame_unwind intelgt_unwinder =
> > +  {
> > +    "intelgt prologue",
> > +    NORMAL_FRAME,			/* type */
> > +    default_frame_unwind_stop_reason,	/* stop_reason */
> > +    intelgt_frame_this_id,		/* this_id */
> > +    nullptr,				/* prev_register */
> > +    nullptr,				/* unwind_data */
> > +    default_frame_sniffer,		/* sniffer */
> > +    nullptr,				/* dealloc_cache */
> > +  };
> 
> This unwinder doesn't do much. Is it necessary? If so, I suggest a
> comment explaining why.

Without this, if a program that has no debug info is debugged, GDB crashes with

  Recursive internal problem.

This unwinder avoids that problem.  I'll add a comment.

> > +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));
> > +
> > +  /* Warn if we're inserting a permanent breakpoint.  */
> > +  if (intelgt::has_breakpoint (bp->shadow_contents))
> > +    warning (_("Re-inserting permanent breakpoint at %s."),
> > +	     paddress (gdbarch, bp->placed_address));
> > +
> > +  /* See comment in mem-break.c on write_inferior_memory.  */
> 
> I wasn't able to find that comment. Has it been removed from GDB?

Seems like so.  I removed the dangling reference to the comment.

> > +  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);
> > +  intelgt_gdbarch_tdep *data
> > +    = gdbarch_tdep<intelgt_gdbarch_tdep> (arch);
> > +
> > +  if (strcmp (name, "framedesc") == 0)
> > +    {
> > +      if (data->framedesc_type != nullptr)
> > +	return data->framedesc_type;
> > +      type *frame = arch_composite_type (arch, "frame_desc", TYPE_CODE_STRUCT);
> > +      append_composite_type_field (frame, "return_ip", bt->builtin_uint32);
> 
> Isn't it better to use a function pointer type? This is what x86_64 and
> aarch64 do:
> 
> (gdb) ptype $pc
> type = void (*)()
> 
> Though I see further below that pointer and address lengths in gdbarch
> are set to 64 bits, so perhaps things aren't very straightforward in
> this architecture...

An "IP" is a 32b offset that has to be combined with a 64b base address to
refer to an actual instruction.  Hence, we preferred to use the plain 32b type.
 
> > +      append_composite_type_field (frame, "return_callmask",
> > +				   bt->builtin_uint32);
> > +      append_composite_type_field (frame, "be_sp", bt->builtin_uint32);
> > +      append_composite_type_field (frame, "be_fp", bt->builtin_uint32);
> > +      append_composite_type_field (frame, "fe_fp", bt->builtin_uint64);
> > +      append_composite_type_field (frame, "fe_sp", bt->builtin_uint64);
> 
> If the 'p' in the fields above mean "pointer", would a void pointer
> work? This is what x86_64 and aarch64 do:
> 
> (gdb) ptype $sp
> type = void *

The fe_sp and fe_fp fields are actual addresses.  It makes sense to use
a void pointer type for them.  I updated their types.  The be_ fields,
however, are 32b values that are meaningful only in combination with other
data.  Let me not change them.

> > +      data->framedesc_type = frame;
> > +      return frame;
> > +    }
> > +  else if (strcmp (name, "ip") == 0)
> > +    return bt->builtin_uint32;
> 
> Same note about using a function pointer type as in return_ip above.

Yes, this is again a 32b value that, as is, does not really point to anything.
After being combined with a base address, it becomes a "PC".

> > +      /* Unconditionally enabled pseudo-registers:  */
> > +      data->enabled_pseudo_regs.push_back ("ip");
> > +      data->enabled_pseudo_regs.push_back ("framedesc");
> > +
> > +      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_ptr_bit (gdbarch, 64);
> > +  set_gdbarch_addr_bit (gdbarch, 64);
> 
> I'm a bit surprised by these, considering that PC is 32 bits. If this is
> correct, then it's worth adding an explanation about the discrepancy.
> Is this why PC is uint32 instead of a pointer type?

PC is still 64b.  The hardware provides a 32b subregister (CR0.2, to be precise)
which is added to a base address called "isabase", to obtain the PC.
The 32b value in CR0.2 is provided as a pseudo register named "ip" for convenience.
intelgt_read_pc and intelgt_write_pc functions compose and decompose PC this way.
Do you think such a comment should be written here?
 
> > +/* Dump the target specific data for this architecture.  */
> > +
> > +static void
> > +intelgt_dump_tdep (gdbarch *gdbarch, ui_file *file)
> > +{
> > +  /* Implement target-specific print output if and
> > +     when gdbarch_tdep is defined for this architecture.  */
> 
> From looking at gdbarch_dump in gdb/gdbarch-gen.c, you can just pass
> nullptr to gdbarch_register instead of having to define a dummy function.
> 
> Though the comment isn't accurate. There is intelgt_gdbarch_tdep, but
> perhaps it's not necessary to dump any of its values?

As far as I remember, it used to be required to pass a non-null function pointer.
It doesn't seem to be case anymore.  Thanks for noting this.  Removing the
intelgt_dump_tdep function.

> > +void _initialize_intelgt_tdep ();
> > +void
> > +_initialize_intelgt_tdep ()
> 
> Update to current style using INIT_GDB_FILE macro.

I couldn't follow.  Could you please clarify or point to an example?

-Baris


Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 87+ messages in thread

* RE: [PATCH v2 05/47] gdb, arch, intelgt: add intelgt arch definitions
  2025-07-08  3:03   ` Thiago Jung Bauermann
@ 2025-07-21 10:49     ` Aktemur, Tankut Baris
  0 siblings, 0 replies; 87+ messages in thread
From: Aktemur, Tankut Baris @ 2025-07-21 10:49 UTC (permalink / raw)
  To: Thiago Jung Bauermann, gdb-patches; +Cc: Metzger, Markus T

On Tuesday, July 8, 2025 5:03 AM, Thiago Jung Bauermann wrote:
> Tankut Baris Aktemur <tankut.baris.aktemur@intel.com> writes:
> 
> > From: Markus Metzger <markus.t.metzger@intel.com>
> >
> > 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>
> > ---
> >  gdb/Makefile.in    |   1 +
> >  gdb/arch/intelgt.c |  77 +++++++++++++++++++++++
> >  gdb/arch/intelgt.h | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 253 insertions(+)
> 
> Just one nit below, but regardless:
> 
> Reviewed-By: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> 
> <snip>
> 
> > +static inline unsigned int
> > +inst_length_compacted ()
> > +{
> > +  return COMPACT_INST_LENGTH;
> > +}
> > +
> > +static inline unsigned int
> > +inst_length_full ()
> > +{
> > +  return MAX_INST_LENGTH;
> > +}
> 
> Not sure I see an advantage in these functions. I suggest removing them
> and using the constants directly.

Ack.  Updated the code locally.

Thank you.
-Baris


Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 87+ messages in thread

* RE: [PATCH v2 07/47] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description
  2025-07-08  4:04   ` Thiago Jung Bauermann
@ 2025-07-21 10:49     ` Aktemur, Tankut Baris
  0 siblings, 0 replies; 87+ messages in thread
From: Aktemur, Tankut Baris @ 2025-07-21 10:49 UTC (permalink / raw)
  To: Thiago Jung Bauermann, gdb-patches; +Cc: Metzger, Markus T

On Tuesday, July 8, 2025 6:04 AM, Thiago Jung Bauermann wrote:
> Tankut Baris Aktemur <tankut.baris.aktemur@intel.com> writes:
> 
> > diff --git a/gdb/xml-tdesc.c b/gdb/xml-tdesc.c
> > index 436c493d4f914eb71a80bcbc08dc2ddd38c72027..61cba42e9eee2ec4cc66e14a120c5bef909e48b0
> 100644
> > --- a/gdb/xml-tdesc.c
> > +++ b/gdb/xml-tdesc.c
> > @@ -137,6 +137,65 @@ tdesc_end_compatible (struct gdb_xml_parser *parser,
> >    tdesc_add_compatible (data->tdesc, arch);
> >  }
> >
> > +/* Handle the end of a <device> element and its value.  */
> 
> s/the end/the start/
> 
> > +static void
> > +tdesc_start_device (struct gdb_xml_parser *parser,
> > +		    const gdb_xml_element *element,
> > +		    void *user_data, std::vector<gdb_xml_value> &attributes)
> > +{
> 
> <snip>
> 
> > +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);
> > +
> > +  string_appendf (tmp, " family=\"%s\"", device->family.c_str ());
> > +  string_appendf (tmp, " model=\"%s\"", device->model.c_str ());
> 
> For all the string attributes in device: isn't it better to check
> whether the string is empty and only emit the attribute if it isn't?

Added the checks.

> > +  if (device->stepping.has_value ())
> > +    string_appendf (tmp, " stepping=\"%d\"", *device->stepping);
> > +
> > +  string_appendf (tmp, " name=\"%s\"", device->name.c_str ());
> > +  string_appendf (tmp, " pci-slot=\"%s\"", device->pci_slot.c_str ());
> > +  string_appendf (tmp, " uuid=\"%s\"", device->uuid.c_str ());
> > +
> > +  if (device->total_cores.has_value ())
> > +    string_appendf (tmp, " total-cores=\"%ld\"", *device->total_cores);
> > +
> > +  if (device->total_threads.has_value ())
> > +    string_appendf (tmp, " total-threads=\"%ld\"", *device->total_threads);
> > +
> > +  if (device->subdevice_id.has_value ())
> > +    string_appendf (tmp, " subdevice-id=\"%d\"", *device->subdevice_id);
> > +
> > +  string_appendf (tmp, "/>");
> > +  add_line (tmp);
> > +}
> > +
> >  /* See gdbsupport/tdesc.h.  */
> >
> >  void
> > diff --git a/gdbsupport/tdesc.h b/gdbsupport/tdesc.h
> > index c9e7603369cbf986187908c2d1f8fbe35a0dc02d..29de4d231f16667c8297bcbe403f683c30a28762
> 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,84 @@ struct tdesc_feature : tdesc_element
> >
> >  typedef std::unique_ptr<tdesc_feature> tdesc_feature_up;
> >
> > +/* The device information in a target description.  */
> > +
> > +struct tdesc_device : tdesc_element
> > +{
> > +  tdesc_device ()
> > +  {
> > +    family = "-";
> > +    model = "-";
> > +    name = "-";
> > +    pci_slot = "-";
> > +    uuid = "-";
> 
> Why aren't the strings left empty?

Not included in this series but we have a patch that defines an "info devices"
command to display the device properties.  It was for convenience to print "-"
if the feature was not included.  To be more generic, it makes sense to initialize
the strings empty and then do the checks at the usage side.

Thanks,
-Baris


Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 87+ messages in thread

* RE: [PATCH v2 10/47] gdb, gdbserver, ze: in-memory libraries
  2025-07-14  2:35   ` Thiago Jung Bauermann
@ 2025-07-31  6:09     ` Metzger, Markus T
  0 siblings, 0 replies; 87+ messages in thread
From: Metzger, Markus T @ 2025-07-31  6:09 UTC (permalink / raw)
  To: Thiago Jung Bauermann; +Cc: gdb-patches, Aktemur, Tankut Baris

Hello Thiago,

Thanks for your review.

>>  <!-- library-list: Root element with versioning -->
>> -<!ELEMENT library-list  (library)*>
>> -<!ATTLIST library-list  version CDATA   #FIXED  "1.0">
>> +<!ELEMENT library-list  (library | in-memory-library)*>
>> +<!ATTLIST library-list  version CDATA   #FIXED  "1.1">
>
>Theoretically, this would cause the parser to reject an XML unless it
>has version="1.1", despite the fact that GDB is still able to parse 1.0
>documents.  Therefore, this should just be changed to CDATA #REQUIRED,
>and we can rely on library_list_start_list to check if the version is
>acceptable.
>
>I said "theoretically" and "would" above because I experimented with it
>(actually, with <library-list-svr4> which is what I can test on Linux)
>and GDB didn't reject an XML reply with a tag that's not present in the
>DTD, and also it didn't care about the value of the version
>attribute. The DTD is simply ignored.
>
>Looking around a bit, I found a comment¹ by an Expat maintainer
>saying:
>
>> The thing is: Expat is a non-validating XML parser.
>
>So I'm not sure what purpose the DTDs have in GDB. Perhaps they're just
>part of the documentation?
>
>If the DTD was enforced, it would be ok to keep version at 1.0 since an
>old GDB would reject a library-list document with in-memory-library
>elements because it wouldn't conform to the expected DTD. As it is
>though, it looks like the version bump is indeed necessary.

IIUC you want this to remain as it is.

>There's another version bump elsewhere in this series though, which I
>think isn't really needed in practice — will there ever be a version of
>GDB in the field which supports version 1.1 but not 1.2?

I don't expect that.  I'll remove the other bump and stay at 1.1.

>> @@ -246,10 +295,35 @@ solib_target_current_sos (void)
>>    for (lm_info_target_up &info : library_list)
>>      {
>>        auto &new_solib = sos.emplace_back ();
>> +      switch (info->location)
>> +	{
>> +	case lm_on_disk:
>> +	  /* We don't need a copy of the name in INFO anymore.  */
>> +	  new_solib.so_name = std::move (info->name);
>> +	  new_solib.so_original_name = new_solib.so_name;
>> +	  break;
>> +
>> +	case lm_in_memory:
>> +	  {
>> +	    if (info->end <= info->begin)
>> +	      error (_("bad in-memory-library location: begin=%s, end=%s"),
>> +		     core_addr_to_string_nz (info->begin),
>> +		     core_addr_to_string_nz (info->end));
>
>Is erroring out better than printing a warning and skipping the bad
>library entry?

Makes sense.  The rest would still work.

>> +	    /* Give it a name although this isn't really needed.  */
>> +	    std::string orig_name
>> +	      = std::string ("in-memory-")
>> +	      + core_addr_to_string_nz (info->begin)
>> +	      + "-"
>> +	      + core_addr_to_string_nz (info->end);
>> +
>> +	    new_solib.so_original_name = orig_name;
>
>I know less about C++ than I would like: would it be better to use
>std::move (orig_name) here, or can we rely on the compiler being smart
>enough to notice that it doesn't need orig_name after this line and
>avoid a copy? If you don't know either, I'd add an std::move just to be
>sure.

We don't really need that local variable.

>> @@ -40,6 +40,17 @@ loaded_dll (process_info *proc, const char *name,
>CORE_ADDR base_addr)
>>    proc->dlls_changed = true;
>>  }
>>
>> +/* Record a newly loaded in-memory DLL at BASE_ADDR for PROC.  */
>
>You should also document the BEGIN and END arguments. In particular, I'm
>interested in learning the difference between BEGIN and BASE_ADDR.

I documented the corresponding fields in struct dll_info.

The arguments of all those functions directly correspond to those fields.
If it is OK, I would not repeat the documentation.

>> +void
>> +loaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
>> +	    CORE_ADDR base_addr)
>> +{
>> +  gdb_assert (proc != nullptr);
>> +  proc->all_dlls.emplace_back (begin, end, base_addr);
>> +  proc->dlls_changed = true;
>> +}
>
>Even though both versions of loaded_dll are small, it's a bit
>unfortunate that they're almost identical. This is because of the choice
>to have two constructors for dll_info and the location member being
>initialized depending on which one is called. I understand that design
>choice, but given that it leads to this duplication of code I think it's
>better to have location explicit in the constructor (or a new
>constructor with location explicitly passed in it) and then one version
>of loaded_dll can simply call the other one (or both can call a third,
>internal loaded_dll version) with an explicit location argument.

It's not just the location.  They would also need to provide all the
arguments that are only relevant for the other location.

And if we ever added a third location type (a section in a fat binary,
maybe), we'd need to touch all that code again to add arguments
for that new type.

I could add a check_ack() function to contain

  if (need_ack && !get_client_state ().vack_library_supported)
    throw_error (NOT_SUPPORTED_ERROR,
		 _("library acknowledgement not supported."));

if that helps.

>> +void
>> +unloaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
>> +	      CORE_ADDR base_addr)
>> +{
>> +  gdb_assert (proc != nullptr);
>> +  auto pred = [&] (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 != UNSPECIFIED_CORE_ADDR
>> +	  && begin == dll.begin
>> +	  && (end == UNSPECIFIED_CORE_ADDR
>> +	      || end == dll.end))
>> +	return true;
>> +
>> +      return false;
>> +    };
>> +
>> +  auto iter = std::find_if (proc->all_dlls.begin (), proc->all_dlls.end (),
>> +			    pred);
>> +
>> +  if (iter == proc->all_dlls.end ())
>> +    /* For some inferiors we might get unloaded_dll events without having
>> +       a corresponding loaded_dll.  In that case, the dll cannot be found
>> +       in ALL_DLL, and there is nothing further for us to do.  */
>> +    return;
>> +  else
>> +    {
>> +      /* DLL has been found so remove the entry and free associated
>> +	 resources.  */
>> +      proc->all_dlls.erase (iter);
>> +      proc->dlls_changed = 1;
>> +    }
>> +}
>
>Here the duplication problem between the two versions of unloaded_dll is
>worse. It's possible do define an unloaded_dll_1 internal function with
>the code above and getting an dll_info::location_t as argument that is
>called by both versions of unloaded_dll.

The predicate is different for the two versions.  I added a function that
takes the predicate as input.

>> +  location_t location;
>>    std::string name;
>> +  CORE_ADDR begin;
>
>Here too it would be nice to have a comment explaining the difference
>between begin and base_addr.

That's where I added the comments describing the fields.

>> +{
>> +  if (dll.end <= dll.begin)
>> +    error (_("bad in-memory-library location: begin=%s, end=%s"),
>> +	   core_addr_to_string_nz (dll.begin),
>> +	   core_addr_to_string_nz (dll.end));
>> +
>> +  gdb::byte_vector buffer (dll.end - dll.begin);
>> +  int errcode = gdb_read_memory (dll.begin, buffer.data (), buffer.size ());
>> +  if (errcode != 0)
>> +    error (_("failed to read in-memory library at %s..%s"),
>> +	   core_addr_to_string_nz (dll.begin),
>> +	   core_addr_to_string_nz (dll.end));
>> +
>> +  std::string name
>> +    = std::string ("gdb-in-memory-solib-")
>> +    + core_addr_to_string_nz (dll.begin)
>> +    + "-"
>> +    + core_addr_to_string_nz (dll.end);
>> +
>> +  gdb_file_up file = gdb_create_tmpfile (name);
>
>This is the only use of gdb_create_tmpfile. I think that instead of
>having files that will be deleted when gdbserver exits, it would be
>better if the files were deleted when they aren't necessary anymore.
>
>gdb_create_tmpfile could return not only a gdb_file_up, but also a
>gdb::unlinker (in an std::pair, I suppose). Then dll_to_tmpfile could
>add it to new member of dll_info with type
>std::optional<gdb::unlinker>. Thus, when the dll_info object is
>destroyed, the tmp file will be deleted. WDYT?
>
>Also, this could be a dll_info::to_tmpfile method instead of a separate
>function.

That sounds good and would make patch 9 obsolete.

>> +static std::string
>> +library_list_version_needed (const std::list<dll_info> &dlls)
>> +{
>> +  const client_state &cs = get_client_state ();
>> +  int major = 1, minor = 0;
>> +
>> +  for (const dll_info &dll : dlls)
>> +    {
>> +      switch (dll.location)
>> +	{
>> +	case dll_info::on_disk:
>> +	  major = std::max (major, 1);
>> +	  minor = std::max (minor, 0);
>> +	  break;
>> +
>> +	case dll_info::in_memory:
>> +	  if (cs.in_memory_library_supported)
>> +	    {
>> +	      major = std::max (major, 1);
>> +	      minor = std::max (minor, 1);
>> +	    }
>> +	  break;
>> +	}
>> +    }
>> +
>> +  return std::to_string (major) + std::string (".") + std::to_string (minor);
>> +}
>
>This makes the version depend on what the currnet list of dll_infos
>needs, not just on the features supported by GDB or gdbserver. If
>there's no in-memory library loaded, then the XML will be version
>1.0. If later an in-memory library is loaded, then the next XML will be
>version 1.1
>
>I expected the version to depend just on the features advertised by GDB
>and gdbserver, so I was a bit surprised. I don't think this is a
>problem, but I just mention here in case someone has an opinion about
>it.

This may be overengineered.  It allows gdbserver with in-memory library
support to work with older GDBs that do not support it.  For our use-case,
such a GDB would not support our architecture, anyway.  From a general
feature perspective, though, it may make sense.

If maintainers agree that we do not want this flexibility, all this tempfile
stuff will go away, simplifying the series a bit.

>> diff --git a/gdb/remote.c b/gdb/remote.c
>> index
>f41129915683194237d1bba56d17df61ae89c063..7d074a5df322d68ded8f96c4
>832bc8c247435a4f 100644
>> --- a/gdb/remote.c
>> +++ b/gdb/remote.c
>> @@ -5956,6 +5956,9 @@ remote_target::remote_query_supported ()
>>  	  != AUTO_BOOLEAN_FALSE)
>>  	remote_query_supported_append (&q, "memory-tagging+");
>>
>> +      remote_query_supported_append
>> +	(&q, "qXfer:libraries:read:in-memory-library+");
>
>This is the only feature in this function that isn't guarded by a "set
>remote foo-packet" command state. Shouldn't it?

This is not a new packet.  This indicates support for a new element in the
qXfer::libraries::read response.

Regards,
Markus.
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* RE: [PATCH v2 11/47] gdb, gdbserver, rsp, ze: acknowledge libraries
  2025-07-16  4:20   ` Thiago Jung Bauermann
@ 2025-07-31  6:09     ` Metzger, Markus T
  0 siblings, 0 replies; 87+ messages in thread
From: Metzger, Markus T @ 2025-07-31  6:09 UTC (permalink / raw)
  To: Thiago Jung Bauermann; +Cc: gdb-patches, Aktemur, Tankut Baris

Hello Thiago,

Thanks for your review.

>> @@ -48084,16 +48141,18 @@ The format of a library list is described by this
>DTD:
>>  @smallexample
>>  <!-- library-list: Root element with versioning -->
>>  <!ELEMENT library-list            (library | in-memory-library)*>
>> -<!ATTLIST library-list            version CDATA   #FIXED  "1.1">
>> +<!ATTLIST library-list            version CDATA        #FIXED  "1.2">
>>  <!ELEMENT library                 (segment*, section*)>
>> -<!ATTLIST library                 name    CDATA   #REQUIRED>
>> +<!ATTLIST library                 name    CDATA        #REQUIRED
>> +                                  ack     (yes | no)   'no'>
>>  <!ELEMENT in-memory-library       (segment*, section*)>
>> -<!ATTLIST in-memory-library       begin    CDATA   #REQUIRED
>> -                                  end      CDATA   #REQUIRED>
>> +<!ATTLIST in-memory-library       begin    CDATA       #REQUIRED
>> +                                  end      CDATA       #REQUIRED
>> +                                  ack      (yes | no)  'no'>
>>  <!ELEMENT segment                 EMPTY>
>> -<!ATTLIST segment                 address CDATA   #REQUIRED>
>> +<!ATTLIST segment                 address CDATA        #REQUIRED>
>>  <!ELEMENT section                 EMPTY>
>> -<!ATTLIST section                 address CDATA   #REQUIRED>
>> +<!ATTLIST section                 address CDATA        #REQUIRED>
>>  @end smallexample
>
>I would move the last column (the one with "#FIXED", "#REQUIRED" and
>'no') a couple more spaces to the right. The change above leaves them
>right next to "(segment*, section*)>" and it feels a bit crowded IMHO.

I assume you mean moving all the existing entries to the right so they all
align again.

>> +static void
>> +ack_dll (process_info *process, dll_info &dll)
>> +{
>> +  gdb_assert (dll.need_ack);
>> +
>> +  switch (dll.location)
>> +    {
>> +    case dll_info::on_disk:
>> +      /* Check if this is a temporary file for an in-memory library.  */
>> +      if (dll.begin == UNSPECIFIED_CORE_ADDR)
>> +	{
>> +	  target_ack_library (process, dll.name.c_str ());
>> +	  dll.need_ack = false;
>> +	  return;
>> +	}
>> +
>> +      [[fallthrough]];
>> +    case dll_info::in_memory:
>> +      target_ack_in_memory_library (process, dll.begin, dll.end);
>> +      dll.need_ack = false;
>> +      return;
>> +    }
>
>I'm confused by this switch statement. So a vAck:library packet will be
>used only if it's for an in-memory library and vAck:in-memory-library
>isn't supported? I would think that a vAck:library would also be used
>for an on-disk library.

vAck:library is the default for on-disk libraries.  There's one special case,
though.  If the target provided an in-memory library and we created a
temporary file for it, GDB will acknowledge it using vAck:library, but we
want to acknowledge it as in-memory library to our target, since that's
what the target provided.

The check is wrong, though.  It should be (dll.begin == 0) as on-disk libraries
are initialized with begin = 0.  We do not expect in-memory libraries to start
at zero.

This is not covered by automated testing since both GDB and gdbserver support
in-memory libraries for our target.  I tested the v3 version manually by disabling
in-memory library support in our GDB.

See my comment in the reply to patch 10.  Since our target does not need that
fall-back, we could remove it and have someone else add it for their target if they
need it.  This way, we will have a natural way of testing it.  But then, testing
combinations of GDB and gdbserver is and will remain a challenge.


>> +ack_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end)
>> +{
>> +  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)
>> +	{
>> +	  /* For root devices with multiple sub-devices, modules with
>> +	     identical start/end addresses may be received for different
>> +	     sub-devices.  Therefore we check for the 'NEED_ACK' flag in
>> +	     the search, too.  */
>
>And is it guaranteed that only one of them will have need_ack set to
>true? Can you explain that in the comment?

This is simply wrong.  I suspect this is a workaround that sneaked in as
a fixup a long time ago when the stack was less mature and we have not
spotted it when we prepared the patch series for submission.

For root devices with multiple sub-devices and mirrored module heaps,
we indeed load the same device library multiple times, but either GDB
represents each sub-device as a separate inferior, so they would be on
different dll lists, or the stack below GDB would be responsible for
presenting a single virtual module to GDB.

In theory, you could load the same library at different base addresses,
so we could argue that we'd want the base address in the comparison.
And maybe the dynamic linker namespace, too.

We support none of that in our target, so this would be completely
untested and arguably overengineered.

In v3, I remove the need_ack check and make loaded_dll() error out
when called with a duplicate in-memory library so we don't run into
silent errors here.

>> +/* Parse vAck packets.  */
>> +
>> +static void
>> +handle_v_ack (char *own_buf)
>> +{
>> +  client_state &cs = get_client_state ();
>> +  char *p;
>> +
>> +  /* Move past vAck: to the first type string.  */
>> +  p = &own_buf[5];
>> +  do
>> +    {
>> +      if (cs.vack_library_supported
>> +	  && (strncmp (p, "library:", strlen ("library:")) == 0))
>> +	{
>> +	  p += strlen ("library:");
>> +
>> +	  /* We expect a single argument: the filename.  */
>> +	  const char *name = p;
>> +	  p = strchr (p, ';');
>> +	  if (p != nullptr)
>> +	    *p++ = '\0';
>> +
>> +	  ack_dll (name);
>
>The documentation for vAck says:
>
>  Acknowledge a ‘;’-separated list of remote stub responses, each with a
>  ‘,’- separated list of arguments defined by its type.
>
>But the above will consider everything between the ':' and ';' to be the
>library name, even if there's a ','. I think you should check whether
>there's a ',' in name, and if so either reject the packet or ignore the
>additional arguments — not sure which is better, I'm leaning towards
>rejecting the packet.

Common practice seems to be to encode filenames in RSP packets
as hex-encoded strings.  I'll follow that.

>> +	}
>> +      else if (cs.vack_in_memory_library_supported
>> +	       && (strncmp (p, "in-memory-library:",
>> +			    strlen ("in-memory-library:")) == 0))
>> +	{
>> +	  p += strlen ("in-memory-library:");
>> +
>> +	  /* We expect two arguments: begin and end address.  */
>> +	  CORE_ADDR begin, end;
>> +
>> +	  begin = (CORE_ADDR) strtoull (p, &p, 16);
>> +	  if (*p != ',')
>> +	    break;
>
>If this break is taken and begin is the last argument in the packet,
>then won't *p == 0 and cause write_ok () to be called below? An error
>should be returned instead.

I agree.  Thanks.

Regards,
Markus.
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 87+ messages in thread

* RE: [PATCH v2 13/47] gdb, remote, ze: fix "$Hc-1#09...Packet received: E01" during startup
  2025-07-18  0:41   ` Thiago Jung Bauermann
@ 2025-08-01  7:55     ` Metzger, Markus T
  0 siblings, 0 replies; 87+ messages in thread
From: Metzger, Markus T @ 2025-08-01  7:55 UTC (permalink / raw)
  To: Thiago Jung Bauermann; +Cc: gdb-patches, Aktemur, Tankut Baris

Hello Thiago,

Thanks for your review.

>Regarding the subject:
>
>Can you expand a bit on what conditions cause this error to happen?  Is
>it broken only for the ze target, or do other targets also get the
>error?
>
>Is it returned when, during handling of 'H', find_thread_ptid can't find
>the "<PID>.-1" thread? I.e., by this code?
>
>  else
>    {
>      /* The ptid represents a lwp/tid.  */
>      if (find_thread_ptid (thread_id) == NULL)
>        {
>          write_enn (cs.own_buf);
>          break;
>        }
>    }

It's been a while, so to answer your question I reverted the patch to
trigger the error again.  And it no longer reproduces.

I still think something is wrong, here.

>> When opening a connection, the remote target does
>>
>>     set_continue_thread (minus_one_ptid);
>
>One interesting aspect of this line is the comment above it:
>
>      /* Let the stub know that we want it to return the thread.  */
>      set_continue_thread (minus_one_ptid);
>
>What does it "the stub [...] to return the thread" mean? I tried reading
>in gdbserver code what it would do if it understood the Hc packet as
>referring to null_ptid or minus_one_ptid, but I couldn't find anything
>that could be described as "returning the thread".
>
>And yet it appears to be the purpose of the line above. I'd say we need
>someone who understands this aspect to review the patch.

Agreed.

>> which results in
>>
>>     $Hc-1#09
>>
>> to be send to gdbserver.  In remote-utils.c:read_ptid (), this is read as
>>
>>     { <current inferior>, -1, 0 }
>
>Nit: I'd say "<current PID>" rather than "<current inferior>".
>
>> and not recognized as minus_one_ptid when handling 'H' in
>>
>> 	  ptid_t thread_id = read_ptid (&cs.own_buf[2], NULL);
>>
>> 	  if (thread_id == null_ptid || thread_id == minus_one_ptid)
>> 	    thread_id = null_ptid;
>>
>> Since minus_one_ptid and null_ptid are treated the same way, this is
>> probably what we want.
>
>Not sure I understood what "this" means in the sentence. If it means "we
>want our Hc packet to be interpreted as thread_id == nullptid in the if
>condition above", then that's not what this patch does (sorry if I
>misunderstood). As you noted, read_ptid can't return a minus_one_ptid,
>but it can't return a null_ptid either.
>
>With the patch applied, read_ptid will return { <current pid>, 0, 0 },
>which ptid_t::is_pid () considers a PID and thus the handling of 'H'
>will call find_any_thread_of_pid, which I'd guess is why this patch
>works.
>
>> In remote_target::remote_resume_with_hc (), we do
>>
>>       if (ptid == minus_one_ptid)
>>         set_continue_thread (any_thread_ptid);
>>       else
>>         set_continue_thread (ptid);
>
>There's no talk of wanting the stub to "return the thread" here though.
>
>> which amounts to the same thing as set_thread () does
>
>Not exactly. In the code below, there's one more "else if" branch:
>
>>       *buf++ = 'H';
>>       *buf++ = gen ? 'g' : 'c';
>>       if (ptid == magic_null_ptid)
>>         xsnprintf (buf, endbuf - buf, "0");
>>       else if (ptid == any_thread_ptid)
>>         xsnprintf (buf, endbuf - buf, "0");
>
>  else if (ptid == minus_one_ptid)
>    xsnprintf (buf, endbuf - buf, "-1");

I argue that on the GDB side, magic_null_ptid and any_thread_ptid are
treated similarly and that on the gdbserver side minus_one_ptid and
null_ptid are treated similarly.

When resuming, GDB even turns minus_one_ptid into any_thread_ptid.

But I believe the issue lies elsewhere.

>So remote_target::set_thread doesn't treat any_thread_ptid and
>minus_one_ptid the same way, even though gdbserver apparently does.
>
>Also, the doc comment of set_thread says: "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."
>
>Here's the talk about asking the stub to "return the thread"
>again. Frustrating.

The comment was added in

    79d7f229011     Use ptid_t.tid to store thread ids instead of ptid_t.pid.

probably based on an earlier comment at the startup set_thread() call in

    c906108c214 (tag: gdb-4_18-branchpoint) Initial creation of sourceware repository

    +  /* Let the stub know that we want it to return the thread.  */
    +  set_thread (-1, 0);
    +
    +  inferior_pid = remote_current_thread (inferior_pid);

The set_continue_thread() (or just set_thread() in earlier versions) is followed by
remote_current_thread() (also today), which sends qC to query the current thread.
This could be what is meant by the target returning the thread.

This query, however, uses the general thread, not the continue thread:

  /* Reply the current thread id.  */
  if (strcmp ("qC", own_buf) == 0 && !disable_packet_qC)
    {
      ptid_t ptid;
      require_running_or_return (own_buf);

      if (cs.general_thread != null_ptid && cs.general_thread != minus_one_ptid)
	ptid = cs.general_thread;
      else
	{
	  init_thread_iter ();
	  ptid = thread_iter->id;
	}

This special handling of the general thread was introduced in

    bd99dc85838     Non-stop mode support.

and it had always been the general thread, not the continue thread.  Before,
there was only the else part.

So, if we wanted to let the target tell us its current thread, we should really be
setting the general thread.  And it doesn't matter if we set it to null_ptid or to
minus_one_ptid.

For Hg we also see some special handling of null_ptid and minus_one_ptid,
which got turned into null_ptid earlier:

	  if (cs.own_buf[1] == 'g')
	    {
	      if (thread_id == null_ptid)
		{
		  /* GDB is telling us to choose any thread.  Check if
		     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)
		    thread = get_first_thread ();
		  thread_id = thread->id;
		}

	      cs.general_thread = thread_id;


Maybe we could also rely on gdbserver initial state, as we're just starting,
and remove set_thread() in remote_target::start_remote_1().

It further looks like read_ptid() needs special handling for the special
thread-ids -1 and 0.

This needs more discussion.

We'll drop this patch from the series as it turned out to be unrelated.

Regards,
Markus.
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 87+ messages in thread

end of thread, other threads:[~2025-08-01  7:56 UTC | newest]

Thread overview: 87+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-12-13 15:59 [PATCH v2 00/47] A new target to debug Intel GPUs Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 01/47] gdb, intelgt: add intelgt as a basic machine Tankut Baris Aktemur
2024-12-16  7:53   ` Jan Beulich
2024-12-17 18:48     ` Aktemur, Tankut Baris
2024-12-18  7:19       ` Jan Beulich
2024-12-20  9:55         ` Aktemur, Tankut Baris
2025-02-03 17:17           ` Aktemur, Tankut Baris
2025-02-04  7:06             ` Jan Beulich
2024-12-13 15:59 ` [PATCH v2 02/47] bfd: add intelgt target to BFD Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 03/47] ld: add intelgt as a target configuration Tankut Baris Aktemur
2024-12-16  7:43   ` Jan Beulich
2024-12-13 15:59 ` [PATCH v2 04/47] opcodes: add intelgt as a configuration Tankut Baris Aktemur
2024-12-16  7:44   ` Jan Beulich
2024-12-17 18:47     ` Aktemur, Tankut Baris
2024-12-18  7:22       ` Jan Beulich
2024-12-20  9:47         ` Aktemur, Tankut Baris
2025-01-03  4:46           ` Simon Marchi
2025-02-03 17:13             ` Aktemur, Tankut Baris
2025-02-04  7:07               ` Jan Beulich
2024-12-13 15:59 ` [PATCH v2 05/47] gdb, arch, intelgt: add intelgt arch definitions Tankut Baris Aktemur
2025-07-08  3:03   ` Thiago Jung Bauermann
2025-07-21 10:49     ` Aktemur, Tankut Baris
2024-12-13 15:59 ` [PATCH v2 06/47] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture Tankut Baris Aktemur
2025-07-08  2:43   ` Thiago Jung Bauermann
2025-07-18 17:43     ` Aktemur, Tankut Baris
2024-12-13 15:59 ` [PATCH v2 07/47] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description Tankut Baris Aktemur
2024-12-13 16:45   ` Eli Zaretskii
2025-07-08  4:04   ` Thiago Jung Bauermann
2025-07-21 10:49     ` Aktemur, Tankut Baris
2024-12-13 15:59 ` [PATCH v2 08/47] gdb, intelgt: add disassemble feature for the Intel GT architecture Tankut Baris Aktemur
2025-07-09  3:12   ` Thiago Jung Bauermann
2024-12-13 15:59 ` [PATCH v2 09/47] gdbsupport, filestuff, ze: temporary files Tankut Baris Aktemur
2025-07-14  1:26   ` Thiago Jung Bauermann
2024-12-13 15:59 ` [PATCH v2 10/47] gdb, gdbserver, ze: in-memory libraries Tankut Baris Aktemur
2025-07-14  2:35   ` Thiago Jung Bauermann
2025-07-31  6:09     ` Metzger, Markus T
2025-07-16  4:08   ` Thiago Jung Bauermann
2024-12-13 15:59 ` [PATCH v2 11/47] gdb, gdbserver, rsp, ze: acknowledge libraries Tankut Baris Aktemur
2024-12-13 16:43   ` Eli Zaretskii
2025-07-16  4:20   ` Thiago Jung Bauermann
2025-07-31  6:09     ` Metzger, Markus T
2024-12-13 15:59 ` [PATCH v2 12/47] gdb, solib, ze: solib_bfd_open_from_target_memory Tankut Baris Aktemur
2025-07-18  0:42   ` Thiago Jung Bauermann
2024-12-13 15:59 ` [PATCH v2 13/47] gdb, remote, ze: fix "$Hc-1#09...Packet received: E01" during startup Tankut Baris Aktemur
2025-07-18  0:41   ` Thiago Jung Bauermann
2025-08-01  7:55     ` Metzger, Markus T
2024-12-13 15:59 ` [PATCH v2 14/47] gdb, infrun, ze: allow saving process events Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 15/47] gdb, ze: add TARGET_WAITKIND_UNAVAILABLE Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 16/47] gdb, infrun, ze: handle stopping unavailable threads Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 17/47] gdb, infrun, ze: allow resuming " Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 18/47] gdb, gdbserver, ze: add U stop reply Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 19/47] gdb, gdbserver, ze: add library notification to " Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 20/47] gdbserver, ze: report TARGET_WAITKIND_UNAVAILABLE events Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 21/47] gdb, ze: handle TARGET_WAITKIND_UNAVAILABLE in stop_all_threads Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 22/47] gdb, remote: handle thread unavailability in print_one_stopped_thread Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 23/47] gdb, remote: do 'remote_add_inferior' in 'remote_notice_new_inferior' earlier Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 24/47] gdb, remote: handle a generic process PID in remote_notice_new_inferior Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 25/47] gdb, remote: handle a generic process PID in process_stop_reply Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 26/47] gdb: use the pid from inferior in setup_inferior Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 27/47] gdb: revise the pid_to_exec_file target op Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 28/47] gdb: load solibs if the target does not have the notion of an exec file Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 29/47] gdbserver: import AC_LIB_HAVE_LINKFLAGS macro into the autoconf script Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 30/47] gdbserver: add a pointer to the owner thread in regcache Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 31/47] gdbserver: dump 'xx...x' in collect_register_as_string for unavailable register Tankut Baris Aktemur
2024-12-23 11:38   ` Aktemur, Tankut Baris
2024-12-23 13:47     ` Luis Machado
2024-12-13 15:59 ` [PATCH v2 32/47] gdbserver: wait for stopped threads in queue_stop_reply_callback Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 33/47] gdbserver: adjust pid after the target attaches Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 34/47] gdb: do not create a thread after a process event Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 35/47] gdb, ze: on a whole process stop, mark all threads as not_resumed Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 36/47] gdb, dwarf, ze: add DW_OP_INTEL_regval_bits Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 37/47] gdbserver: allow configuring for a heterogeneous target Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 38/47] gdbserver, ze, intelgt: introduce ze-low and intel-ze-low targets Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 39/47] testsuite, sycl: add SYCL support Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 40/47] testsuite, sycl: add test for backtracing inside a kernel Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 41/47] testsuite, sycl: add test for 'info locals' and 'info args' Tankut Baris Aktemur
2024-12-13 15:59 ` [PATCH v2 42/47] testsuite, sycl: add tests for stepping and accessing data elements Tankut Baris Aktemur
2024-12-13 16:00 ` [PATCH v2 43/47] testsuite, sycl: add test for 1-D and 2-D parallel_for kernels Tankut Baris Aktemur
2024-12-13 16:00 ` [PATCH v2 44/47] testsuite, sycl: add test for scheduler-locking Tankut Baris Aktemur
2024-12-13 16:00 ` [PATCH v2 45/47] testsuite, arch, intelgt: add a disassembly test Tankut Baris Aktemur
2024-12-13 16:00 ` [PATCH v2 46/47] testsuite, arch, intelgt: add intelgt-program-bp.exp Tankut Baris Aktemur
2024-12-13 16:00 ` [PATCH v2 47/47] testsuite, sycl: test canceling a stepping flow Tankut Baris Aktemur
2025-02-07 10:18 ` [PATCH v2 00/47] A new target to debug Intel GPUs Aktemur, Tankut Baris
2025-05-08  7:40   ` Aktemur, Tankut Baris
2025-05-26  8:03     ` Aktemur, Tankut Baris
2025-06-17 12:22       ` Aktemur, Tankut Baris
2025-07-03 12:55   ` Aktemur, Tankut Baris

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox