* [PATCH 00/46] A new target to debug Intel GPUs
@ 2024-07-02 11:42 Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 01/46] gdb, intelgt: add intelgt as a basic machine Tankut Baris Aktemur
` (46 more replies)
0 siblings, 47 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
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
GPU threads operate in a SIMD (single instruction multiple data)
manner: they are vectorized, where each SIMD lane (also known as
"execution channel") executes the same instruction but using different
data values. SIMD 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.
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 in the process
of being submitted to upstream at
https://gitlab.freedesktop.org/miku/kernel/-/tree/eudebug-dev
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 [1] 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.
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)
We also submit patches for the testsuite, where we introduce the
infrastructure and a number of test cases using SYCL.
Regards,
Baris
[1]: https://www.khronos.org/sycl/
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 (25):
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: revise the pid_to_exec_file target op
gdb: use the pid from inferior in setup_inferior
gdb: load solibs even when exec_bfd does not exist
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
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 | 7 +-
gdb/Makefile.in | 8 +-
gdb/NEWS | 29 +
gdb/arch/intelgt.c | 78 +
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 | 57 +-
gdb/inferior.h | 6 +
gdb/infrun.c | 125 +-
gdb/intelgt-tdep.c | 1110 ++++++
gdb/nat/fork-inferior.c | 10 +
gdb/regcache.c | 6 +-
gdb/remote.c | 196 +-
gdb/solib-target.c | 147 +-
gdb/solib.c | 103 +-
gdb/solist.h | 25 +-
gdb/target-delegates.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.arch/intelgt-disassemble.exp | 83 +
gdb/testsuite/gdb.arch/intelgt-program-bp.exp | 104 +
gdb/testsuite/gdb.arch/sycl-simple.cpp | 42 +
gdb/testsuite/gdb.sycl/break.exp | 62 +
gdb/testsuite/gdb.sycl/break2.exp | 65 +
gdb/testsuite/gdb.sycl/call-stack.cpp | 92 +
gdb/testsuite/gdb.sycl/call-stack.exp | 189 ++
.../gdb.sycl/info-locals-and-args.exp | 77 +
gdb/testsuite/gdb.sycl/parallel-for-1D.cpp | 72 +
gdb/testsuite/gdb.sycl/parallel-for-1D.exp | 54 +
gdb/testsuite/gdb.sycl/parallel-for-2D.cpp | 73 +
gdb/testsuite/gdb.sycl/parallel-for-2D.exp | 54 +
gdb/testsuite/gdb.sycl/scheduler-locking.exp | 66 +
gdb/testsuite/gdb.sycl/single-task.cpp | 50 +
gdb/testsuite/gdb.sycl/step-canceled.exp | 85 +
gdb/testsuite/gdb.sycl/step-into-function.exp | 46 +
gdb/testsuite/gdb.sycl/step-parallel-for.exp | 62 +
gdb/testsuite/gdb.sycl/step.exp | 50 +
gdb/testsuite/gdb.threads/killed-outside.exp | 4 +
gdb/testsuite/lib/gdb.exp | 29 +-
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 | 382 +++
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 | 4 +
gdbserver/dll.cc | 159 +-
gdbserver/dll.h | 36 +-
gdbserver/intelgt-ze-low.cc | 1039 ++++++
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 | 3006 +++++++++++++++++
gdbserver/ze-low.h | 492 +++
gdbsupport/filestuff.cc | 39 +
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 +
113 files changed, 11243 insertions(+), 167 deletions(-)
mode change 100644 => 100755 bfd/config.bfd
create mode 100644 bfd/cpu-intelgt.c
create mode 100644 bfd/elf64-intelgt.c
create mode 100644 gdb/arch/intelgt.c
create mode 100644 gdb/arch/intelgt.h
create mode 100755 gdb/intelgt-tdep.c
create mode 100755 gdb/testsuite/boards/intel-offload.exp
create mode 100644 gdb/testsuite/gdb.arch/intelgt-disassemble.exp
create mode 100644 gdb/testsuite/gdb.arch/intelgt-program-bp.exp
create mode 100644 gdb/testsuite/gdb.arch/sycl-simple.cpp
create mode 100644 gdb/testsuite/gdb.sycl/break.exp
create mode 100644 gdb/testsuite/gdb.sycl/break2.exp
create mode 100644 gdb/testsuite/gdb.sycl/call-stack.cpp
create mode 100644 gdb/testsuite/gdb.sycl/call-stack.exp
create mode 100644 gdb/testsuite/gdb.sycl/info-locals-and-args.exp
create mode 100644 gdb/testsuite/gdb.sycl/parallel-for-1D.cpp
create mode 100644 gdb/testsuite/gdb.sycl/parallel-for-1D.exp
create mode 100644 gdb/testsuite/gdb.sycl/parallel-for-2D.cpp
create mode 100644 gdb/testsuite/gdb.sycl/parallel-for-2D.exp
create mode 100644 gdb/testsuite/gdb.sycl/scheduler-locking.exp
create mode 100644 gdb/testsuite/gdb.sycl/single-task.cpp
create mode 100644 gdb/testsuite/gdb.sycl/step-canceled.exp
create mode 100644 gdb/testsuite/gdb.sycl/step-into-function.exp
create mode 100644 gdb/testsuite/gdb.sycl/step-parallel-for.exp
create mode 100644 gdb/testsuite/gdb.sycl/step.exp
create mode 100644 gdb/testsuite/lib/sycl-devices.cpp
create mode 100644 gdb/testsuite/lib/sycl-hello.cpp
create mode 100644 gdb/testsuite/lib/sycl-util.cpp
create mode 100644 gdb/testsuite/lib/sycl.exp
create mode 100644 gdbserver/intelgt-ze-low.cc
create mode 100644 gdbserver/ze-low.cc
create mode 100644 gdbserver/ze-low.h
create mode 100644 include/elf/intelgt.h
--
2.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] 77+ messages in thread
* [PATCH 01/46] gdb, intelgt: add intelgt as a basic machine
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-07-02 15:49 ` Maciej W. Rozycki
2024-07-02 11:42 ` [PATCH 02/46] bfd: add intelgt target to BFD Tankut Baris Aktemur
` (45 subsequent siblings)
46 siblings, 1 reply; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
Add 'intelgt' as a basic machine to config.sub.
---
config.sub | 1 +
1 file changed, 1 insertion(+)
diff --git a/config.sub b/config.sub
index defe52c0c87..b4638e4d586 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] 77+ messages in thread
* [PATCH 02/46] bfd: add intelgt target to BFD
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 01/46] gdb, intelgt: add intelgt as a basic machine Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-10-15 22:15 ` Thiago Jung Bauermann
2024-07-02 11:42 ` [PATCH 03/46] ld: add intelgt as a target configuration Tankut Baris Aktemur
` (44 subsequent siblings)
46 siblings, 1 reply; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
From: Natalia Saiapova <natalia.saiapova@intel.com>
Add description of IntelGT target to BFD. Describe its relocation
types.
---
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(-)
mode change 100644 => 100755 bfd/config.bfd
create mode 100644 bfd/cpu-intelgt.c
create mode 100644 bfd/elf64-intelgt.c
create mode 100644 include/elf/intelgt.h
diff --git a/bfd/Makefile.am b/bfd/Makefile.am
index 0dc733eaba9..6f8b622838e 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 \
@@ -203,6 +204,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 b3d97d478ea..e90244f1486 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 \
@@ -669,6 +670,7 @@ ALL_MACHINES_CFILES = \
cpu-i386.c \
cpu-ia64.c \
cpu-iamcu.c \
+ cpu-intelgt.c \
cpu-ip2k.c \
cpu-iq2000.c \
cpu-kvx.c \
@@ -1028,6 +1030,7 @@ BFD64_BACKENDS = \
elf64-hppa.lo \
elf64-ia64-vms.lo \
elf64-ia64.lo \
+ elf64-intelgt.lo \
elf64-kvx.lo \
elf64-loongarch.lo \
elf64-mips.lo \
@@ -1499,6 +1502,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-i386.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-ia64.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-iamcu.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-intelgt.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-ip2k.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-iq2000.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-kvx.Plo@am__quote@
diff --git a/bfd/archures.c b/bfd/archures.c
index 94118b8d2cf..2fc0b7364d3 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
@@ -654,6 +656,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;
@@ -743,6 +746,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 e3b5a8b8522..1c789a6dfb4 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -1660,6 +1660,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
@@ -7513,6 +7515,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 6553aac1e99..ce223942f6b
--- a/bfd/config.bfd
+++ b/bfd/config.bfd
@@ -196,6 +196,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 6458974951e..a53ea9bd380 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 6bcfd1b7368..92c140c50de 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 00000000000..dd78e7260e7
--- /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 00000000000..50ccbac0506
--- /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 5e8ed9eeefe..d8d87eff3c3 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -3674,6 +3674,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 a187afe9b56..f750ada4ef4 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -8367,6 +8367,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 0d5d73ba462..8fecb1ac50d 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;
@@ -1123,6 +1124,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 5d1cf9c3388..c7ac981cf63 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)
@@ -15549,6 +15554,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;
@@ -15688,6 +15695,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 00000000000..16fbf9ce4f7
--- /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] 77+ messages in thread
* [PATCH 03/46] ld: add intelgt as a target configuration
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 01/46] gdb, intelgt: add intelgt as a basic machine Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 02/46] bfd: add intelgt target to BFD Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 04/46] opcodes: add intelgt as a configuration Tankut Baris Aktemur
` (43 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
Add 'intelgt-*-elf' as a target configuration for ld.
---
ld/configure.tgt | 2 ++
1 file changed, 2 insertions(+)
diff --git a/ld/configure.tgt b/ld/configure.tgt
index f937f78b876..f1b614a7fe1 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] 77+ messages in thread
* [PATCH 04/46] opcodes: add intelgt as a configuration
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (2 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 03/46] ld: add intelgt as a target configuration Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 05/46] gdb, arch, intelgt: add intelgt arch definitions Tankut Baris Aktemur
` (42 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
Intel GT does not use the opcodes lib for disassembling instructions.
Therefore, add the simplest configuration to satisfy building without
errors.
---
opcodes/configure | 1 +
opcodes/configure.ac | 1 +
2 files changed, 2 insertions(+)
diff --git a/opcodes/configure b/opcodes/configure
index 050fd5ff0ff..e864b995564 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 4d918e3ef9b..fbea5bbd5f0 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] 77+ messages in thread
* [PATCH 05/46] gdb, arch, intelgt: add intelgt arch definitions
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (3 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 04/46] opcodes: add intelgt as a configuration Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-07-03 17:17 ` Guinevere Larsen
2024-10-15 22:44 ` Thiago Jung Bauermann
2024-07-02 11:42 ` [PATCH 06/46] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture Tankut Baris Aktemur
` (41 subsequent siblings)
46 siblings, 2 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
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/arch/intelgt.c | 78 ++++++++++++++++++++
gdb/arch/intelgt.h | 175 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 253 insertions(+)
create mode 100644 gdb/arch/intelgt.c
create mode 100644 gdb/arch/intelgt.h
diff --git a/gdb/arch/intelgt.c b/gdb/arch/intelgt.c
new file mode 100644
index 00000000000..0fc82aa27be
--- /dev/null
+++ b/gdb/arch/intelgt.c
@@ -0,0 +1,78 @@
+/* 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 "gdbsupport/common-defs.h"
+#include "intelgt.h"
+#include <stdlib.h>
+
+namespace intelgt {
+
+/* Get the bit at POS in INST. */
+
+bool
+get_inst_bit (const gdb_byte inst[], int pos)
+{
+ if (pos < 0 || (MAX_INST_LENGTH * 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_byte inst[], int pos)
+{
+ if (pos < 0 || (MAX_INST_LENGTH * 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_byte inst[], int pos)
+{
+ if (pos < 0 || (MAX_INST_LENGTH * 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 00000000000..a4154ddb8d3
--- /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 (const gdb_byte inst[], int pos);
+
+/* Set the bit at POS in INST. */
+
+bool set_inst_bit (gdb_byte inst[], int pos);
+
+/* Clear the bit at POS in INST. */
+
+bool clear_inst_bit (gdb_byte inst[], int pos);
+
+static inline bool
+is_compacted_inst (const gdb_byte inst[])
+{
+ /* Check the CmptCtrl flag (bit 29). */
+ return inst[3] & 0x20;
+}
+
+static inline int
+breakpoint_bit_offset (const gdb_byte inst[])
+{
+ return (is_compacted_inst (inst) ? 7 : 30);
+}
+
+static inline bool
+set_breakpoint (gdb_byte inst[])
+{
+ return set_inst_bit (inst, breakpoint_bit_offset (inst));
+}
+
+static inline bool
+clear_breakpoint (gdb_byte inst[])
+{
+ return clear_inst_bit (inst, breakpoint_bit_offset (inst));
+}
+
+static inline bool
+has_breakpoint (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 (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] 77+ messages in thread
* [PATCH 06/46] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (4 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 05/46] gdb, arch, intelgt: add intelgt arch definitions Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-08-26 16:27 ` Lancelot SIX
2024-10-15 23:35 ` Thiago Jung Bauermann
2024-07-02 11:42 ` [PATCH 07/46] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description Tankut Baris Aktemur
` (40 subsequent siblings)
46 siblings, 2 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
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, IA, 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 | 2 +
gdb/configure.tgt | 5 +
gdb/intelgt-tdep.c | 987 +++++++++++++++++++++++++++++++++++++++++++++
gdb/regcache.c | 6 +-
4 files changed, 999 insertions(+), 1 deletion(-)
create mode 100755 gdb/intelgt-tdep.c
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 1c697bf0ab1..830311100b0 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -750,11 +750,13 @@ 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 \
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 8326c458eb1..5764db9f199 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -356,6 +356,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 00000000000..83cddec8d4b
--- /dev/null
+++ b/gdb/intelgt-tdep.c
@@ -0,0 +1,987 @@
+/* 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 "defs.h"
+#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;
+
+#define dprintf(...) \
+ do \
+ { \
+ if (intelgt_debug) \
+ { \
+ gdb_printf (gdb_stdlog, "%s: ", __func__); \
+ gdb_printf (gdb_stdlog, __VA_ARGS__); \
+ gdb_printf (gdb_stdlog, "\n"); \
+ } \
+ } \
+ while (0)
+
+/* Regnum pair describing the assigned regnum range for a single
+ regset. */
+
+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);
+
+/* The 'gdbarch_data' stuff specific for this architecture. */
+
+struct intelgt_gdbarch_data
+{
+ /* $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-register 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_data ()
+ {
+ memset (®set_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;
+ }
+};
+
+static const registry<gdbarch>::key<intelgt_gdbarch_data>
+ intelgt_gdbarch_data_handle;
+
+static intelgt_gdbarch_data *
+get_intelgt_gdbarch_data (gdbarch *gdbarch)
+{
+ intelgt_gdbarch_data *result = intelgt_gdbarch_data_handle.get (gdbarch);
+ if (result == nullptr)
+ result = intelgt_gdbarch_data_handle.emplace (gdbarch);
+ return result;
+}
+
+/* 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, size_t size, 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 + size > regsize)
+ error (_("%s[%ld:%ld] is outside the range of %s[%d:0]."),
+ regname, (offset + size - 1), offset, regname, (regsize - 1));
+
+ register_status reg_status
+ = regcache->cooked_read_part (regnum, offset, size, 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 for the register listing in the target
+ description to be in the same order in each regeset as the
+ intended DWARF numbering order. Currently this is 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: [BEGIN, END), where BEGIN 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_data *data = get_intelgt_gdbarch_data (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)
+{
+ dprintf ("start_pc: %lx", 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);
+
+ dprintf ("post prologue pc: %lx", 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 (gdbarch *gdbarch, value *function,
+ type *valtype, regcache *regcache,
+ gdb_byte *readbuf, const gdb_byte *writebuf)
+{
+ gdb_assert_not_reached ("intelgt_return_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_data *data = get_intelgt_gdbarch_data (gdbarch);
+
+ int actual_regnum = data->framedesc_base_regnum ();
+
+ /* Unwind the actual GRF register. */
+ return frame_unwind_register_value (this_frame, actual_regnum);
+}
+
+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_data *data = get_intelgt_gdbarch_data (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);
+ dprintf ("prev_ip: %lx", 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: Other tdeps populate and use the cache. */
+
+ /* Try to use symbol information to get the current start address. */
+ CORE_ADDR func;
+
+ if (get_frame_func_if_available (this_frame, &func))
+ {
+ /* Use the current PC as a fallback if no symbol info is available. */
+ if (func == 0)
+ func = get_frame_pc (this_frame);
+
+ /* FIXME: Because there is no full notion of stack, it
+ should be OK to ignore the SP reg. Currently, we cannot use SP
+ even if we want to, because SP's size is 16 bytes whereas
+ CORE_ADDR is 8. */
+ *this_id = frame_id_build_unavailable_stack (func);
+ }
+ else
+ *this_id = outer_frame_id;
+}
+
+static value *
+intelgt_frame_prev_register (const frame_info_ptr &this_frame,
+ void **this_prologue_cache, int regnum)
+{
+ dprintf ("regnum %d", regnum);
+
+ gdbarch *arch = get_frame_arch (this_frame);
+ /* FIXME: Do the values below exist in an ABI? */
+ constexpr int STORAGE_REG_RET_PC = 1;
+ intelgt_gdbarch_data *data = get_intelgt_gdbarch_data (arch);
+ int STORAGE_REG_SP = data->framedesc_base_regnum ();
+
+ if (regnum == intelgt_pseudo_register_num (arch, "ip"))
+ return frame_unwind_got_register (this_frame, regnum,
+ STORAGE_REG_RET_PC);
+ else if (regnum == gdbarch_sp_regnum (arch))
+ return frame_unwind_got_register (this_frame, regnum,
+ STORAGE_REG_SP);
+ else
+ return frame_unwind_got_register (this_frame, regnum, regnum);
+}
+
+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 */
+ intelgt_frame_prev_register, /* 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, struct bp_target_info *bp)
+{
+ dprintf ("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. */
+ dprintf ("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)
+ dprintf ("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)
+{
+ dprintf ("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)
+ dprintf ("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)
+{
+ dprintf ("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. */
+ dprintf ("Failed to read memory at %s (%s).",
+ paddress (gdbarch, pc), strerror (err));
+ return err;
+ }
+
+ const bool is_bkpt = intelgt::has_breakpoint (inst);
+
+ dprintf ("%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)
+{
+ dprintf ("*pcptr: %lx", *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)
+{
+ dprintf ("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;
+}
+
+/* Utility function to lookup 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_data *data = get_intelgt_gdbarch_data (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 ());
+}
+
+static CORE_ADDR
+intelgt_read_pc (readable_regcache *regcache)
+{
+ gdbarch *arch = regcache->arch ();
+ intelgt_gdbarch_data *data = get_intelgt_gdbarch_data (arch);
+
+ /* Instruction pointer is stored in CR0.2. */
+ uint32_t ip;
+ intelgt_read_register_part (regcache, data->cr0_regnum,
+ sizeof (uint32_t) * 2, sizeof (uint32_t),
+ (gdb_byte *) &ip, _("Cannot compute PC."));
+
+ /* Program counter is $ip + $isabase. */
+ CORE_ADDR isabase = intelgt_get_isabase (regcache);
+ return isabase + ip;
+}
+
+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 0x%lx, out of range", pc);
+
+ intelgt_gdbarch_data *data = get_intelgt_gdbarch_data (arch);
+
+ /* Instruction pointer is stored in CR0.2. */
+ uint32_t ip = pc - isabase;
+ regcache->cooked_write_part (data->cr0_regnum, sizeof (uint32_t) * 2,
+ sizeof (uint32_t), (gdb_byte *) &ip);
+}
+
+/* Return the name of pseudo-register REGNUM. */
+
+static const char *
+intelgt_pseudo_register_name (gdbarch *arch, int regnum)
+{
+ intelgt_gdbarch_data *data = get_intelgt_gdbarch_data (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_data *data = get_intelgt_gdbarch_data (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_data *data = get_intelgt_gdbarch_data (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_data *data = get_intelgt_gdbarch_data (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_data *data = get_intelgt_gdbarch_data (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, nullptr);
+ intelgt_gdbarch_data *data = get_intelgt_gdbarch_data (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 (gdbarch, intelgt_return_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);
+
+ 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 f04354d822f..dc9266a7adc 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -1932,9 +1932,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] 77+ messages in thread
* [PATCH 07/46] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (5 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 06/46] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-07-02 13:28 ` Eli Zaretskii
2024-07-02 11:42 ` [PATCH 08/46] gdb, intelgt: add disassemble feature for the Intel GT architecture Tankut Baris Aktemur
` (39 subsequent siblings)
46 siblings, 1 reply; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
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>
---
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 47677cb773a..61cf5e709d3 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -224,6 +224,14 @@ qIsAddressTagged
file is about, this new packet provides a more generic way to perform such
a check.
+* 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 14
* GDB now supports the AArch64 Scalable Matrix Extension 2 (SME2), which
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 86cd420832a..792f1115871 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -48360,6 +48360,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
@@ -48457,6 +48458,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 d07703fca8b..4afaffc6d0c 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 4f210441623..ce2912ffe5c 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;
@@ -645,6 +648,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 436c493d4f9..61cba42e9ee 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 3265793da96..ad9812158ee 100644
--- a/gdbserver/tdesc.cc
+++ b/gdbserver/tdesc.cc
@@ -192,6 +192,22 @@ set_tdesc_osabi (struct target_desc *target_desc, const char *name)
/* 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 534b8b000f5..ee94d9cdfe4 100644
--- a/gdbserver/tdesc.h
+++ b/gdbserver/tdesc.h
@@ -59,6 +59,9 @@ struct target_desc final : tdesc_element
/* The value of <osabi> element in the XML, replying GDB. */
const char *osabi = NULL;
+ /* 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 080d39c485d..ae7a7b36c7c 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 fa9431b5b65..4633771cae0 100644
--- a/gdbsupport/tdesc.h
+++ b/gdbsupport/tdesc.h
@@ -24,6 +24,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. */
@@ -54,6 +55,9 @@ class tdesc_element_visitor
virtual void visit (const tdesc_reg *e)
{}
+
+ virtual void visit (const tdesc_device *e)
+ {}
};
class tdesc_element
@@ -313,6 +317,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
@@ -345,6 +427,13 @@ void set_tdesc_osabi (target_desc *target_desc, const char *name);
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,
@@ -437,6 +526,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] 77+ messages in thread
* [PATCH 08/46] gdb, intelgt: add disassemble feature for the Intel GT architecture.
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (6 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 07/46] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 09/46] gdbsupport, filestuff, ze: temporary files Tankut Baris Aktemur
` (38 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
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 | 123 ++++++++++
gdb/top.c | 10 +
7 files changed, 720 insertions(+), 3 deletions(-)
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 830311100b0..5b982595c4b 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 0c144c8918b..18ac2290e68 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 15ececfefa2..79899111f8b 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
@@ -29018,6 +29026,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.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 e70edb74578..8bf78c25b26 100644
--- a/gdb/configure.ac
+++ b/gdb/configure.ac
@@ -1284,6 +1284,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.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 14b7fb30bad..a5952f235d4 100644
--- a/gdb/disasm-selftests.c
+++ b/gdb/disasm-selftests.c
@@ -116,6 +116,10 @@ get_test_insn (struct gdbarch *gdbarch, size_t *len)
*len = bplen;
}
break;
+ case bfd_arch_intelgt:
+ /* The intelgt architecture needs to initialize the gdbarch with
+ an IGA context to be able to use the disassembler. */
+ return insn;
case bfd_arch_i386:
{
const struct bfd_arch_info *info = gdbarch_bfd_arch_info (gdbarch);
diff --git a/gdb/intelgt-tdep.c b/gdb/intelgt-tdep.c
index 83cddec8d4b..94f69390c14 100755
--- a/gdb/intelgt-tdep.c
+++ b/gdb/intelgt-tdep.c
@@ -32,6 +32,10 @@
#include "inferior.h"
#include "user-regs.h"
#include <algorithm>
+#include "disasm.h"
+#if defined (HAVE_LIBIGA64)
+#include "iga.h"
+#endif /* defined (HAVE_LIBIGA64) */
/* Global debug flag. */
static bool intelgt_debug = false;
@@ -119,6 +123,11 @@ struct intelgt_gdbarch_data
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
};
static const registry<gdbarch>::key<intelgt_gdbarch_data>
@@ -548,6 +557,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)
+{
+ 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_byte *) insn.get ()))
+ {
+ 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
+ = get_intelgt_gdbarch_data (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_byte *) insn.get ()))
+ 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 lookup the pseudo-register number by name. Exact
amount of pseudo-registers may differ and thus fixed constants can't be
used for this. */
@@ -874,6 +954,46 @@ intelgt_gdbarch_init (gdbarch_info info, gdbarch_list *arches)
gdbarch *gdbarch = gdbarch_alloc (&info, nullptr);
intelgt_gdbarch_data *data = get_intelgt_gdbarch_data (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);
@@ -948,6 +1068,9 @@ intelgt_gdbarch_init (gdbarch_info info, gdbarch_list *arches)
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;
}
diff --git a/gdb/top.c b/gdb/top.c
index d6bf1d448d5..a1510855051 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -1528,6 +1528,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] 77+ messages in thread
* [PATCH 09/46] gdbsupport, filestuff, ze: temporary files
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (7 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 08/46] gdb, intelgt: add disassemble feature for the Intel GT architecture Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-08-31 0:11 ` Lancelot SIX
2024-07-02 11:42 ` [PATCH 10/46] gdb, gdbserver, ze: in-memory libraries Tankut Baris Aktemur
` (37 subsequent siblings)
46 siblings, 1 reply; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
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 | 39 +++++++++++++++++++++++++++++++++++++++
gdbsupport/filestuff.h | 6 ++++++
2 files changed, 45 insertions(+)
diff --git a/gdbsupport/filestuff.cc b/gdbsupport/filestuff.cc
index 5c427e11b02..0ffc8531095 100644
--- a/gdbsupport/filestuff.cc
+++ b/gdbsupport/filestuff.cc
@@ -17,12 +17,15 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "filestuff.h"
+#include "pathstuff.h"
#include "gdb_vecs.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>
@@ -545,3 +548,39 @@ 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 ()
+ {
+ for (const std::string& file : files)
+ unlink (file.c_str ());
+ }
+};
+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"), name.data ());
+
+ gdb_file_up file { fd.to_file ("wb") };
+ if (file.get () == nullptr)
+ error (_("failed to open %s"), name.data ());
+
+ base = name.data ();
+ tmpfilekeeper.files.emplace_back (base);
+
+ return file;
+}
diff --git a/gdbsupport/filestuff.h b/gdbsupport/filestuff.h
index e2ee141d46f..6b7d1ecf935 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] 77+ messages in thread
* [PATCH 10/46] gdb, gdbserver, ze: in-memory libraries
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (8 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 09/46] gdbsupport, filestuff, ze: temporary files Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-07-02 13:20 ` Eli Zaretskii
2024-07-02 11:42 ` [PATCH 11/46] gdb, gdbserver, rsp, ze: acknowledge libraries Tankut Baris Aktemur
` (36 subsequent siblings)
46 siblings, 1 reply; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
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.
---
gdb/NEWS | 8 +++
gdb/doc/gdb.texinfo | 40 +++++++++----
gdb/features/library-list.dtd | 8 ++-
gdb/remote.c | 3 +
gdb/solib-target.c | 90 +++++++++++++++++++++++++++--
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, 348 insertions(+), 28 deletions(-)
diff --git a/gdb/NEWS b/gdb/NEWS
index 61cf5e709d3..f0c70d69bb6 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -13,6 +13,14 @@
This may cause breakage when using an incompatible libc, like uclibc or
newlib, or an older glibc.
+* New remote packets
+
+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.
+
*** 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 792f1115871..67fd1a9a615 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -47836,9 +47836,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
@@ -47850,6 +47851,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:
@@ -47861,6 +47866,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:
@@ -47878,14 +47893,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 98ed7bc28dc..f55071c8e90 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 90a4bd57a82..2c8e1b951ae 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -5941,6 +5941,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 6563da05a47..25a310244d2 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)
{
solib *new_solib = new solib;
+ 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);
/* Add it to the list. */
@@ -412,4 +486,10 @@ const solib_ops solib_target_so_ops =
solib_target_open_symbol_file_object,
solib_target_in_dynsym_resolve_code,
solib_bfd_open,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ gdb_bfd_open_from_target_memory,
};
diff --git a/gdb/solib.c b/gdb/solib.c
index 881f0d96502..d2520d1be5b 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -545,8 +545,30 @@ 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"));
+
gdb::unique_xmalloc_ptr<char> build_id_hexstr
= get_cbfd_soname_build_id (current_program_space->cbfd,
so.so_name.c_str ());
@@ -567,6 +589,7 @@ solib_map_sections (solib &so)
}
if (abfd == nullptr || mismatch)
{
+ gdb::unique_xmalloc_ptr<char> filename;
scoped_fd fd = debuginfod_exec_query (
(const unsigned char *) build_id_hexstr.get (), 0,
so.so_name.c_str (), &filename);
diff --git a/gdb/solist.h b/gdb/solist.h
index f0d22080de1..0021f5001aa 100644
--- a/gdb/solist.h
+++ b/gdb/solist.h
@@ -65,9 +65,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.
@@ -167,6 +172,12 @@ struct solib_ops
NULL, in which case no specific preprocessing is necessary
for this target. */
void (*handle_event) (void);
+
+ /* 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 ff637a03fed..b00820b8405 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;
@@ -90,6 +104,51 @@ unloaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
}
}
+/* 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;
+ }
+}
+
void
clear_dlls (void)
{
diff --git a/gdbserver/dll.h b/gdbserver/dll.h
index 30c6a2c65d0..051c30ff8ac 100644
--- a/gdbserver/dll.h
+++ b/gdbserver/dll.h
@@ -24,11 +24,24 @@ 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;
};
@@ -36,8 +49,12 @@ extern void clear_dlls (void);
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 30d05177452..15b8aba2281 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -1868,6 +1868,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
@@ -1881,13 +1972,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";
@@ -2701,6 +2792,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. */
@@ -2731,6 +2824,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 eabedb95679..fd75ee6e7a4 100644
--- a/gdbserver/server.h
+++ b/gdbserver/server.h
@@ -169,6 +169,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] 77+ messages in thread
* [PATCH 11/46] gdb, gdbserver, rsp, ze: acknowledge libraries
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (9 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 10/46] gdb, gdbserver, ze: in-memory libraries Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-07-02 13:25 ` Eli Zaretskii
2024-07-02 11:42 ` [PATCH 12/46] gdb, solib, ze: solib_bfd_open_from_target_memory Tankut Baris Aktemur
` (35 subsequent siblings)
46 siblings, 1 reply; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
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.c | 50 ++++++++++++++++
gdb/target.c | 16 +++++
gdb/target.h | 21 +++++++
gdbserver/dll.cc | 104 +++++++++++++++++++++++++++++++--
gdbserver/dll.h | 25 +++++---
gdbserver/server.cc | 107 +++++++++++++++++++++++++++++++---
gdbserver/server.h | 4 ++
gdbserver/target.cc | 14 +++++
gdbserver/target.h | 20 +++++++
16 files changed, 589 insertions(+), 35 deletions(-)
diff --git a/gdb/NEWS b/gdb/NEWS
index f0c70d69bb6..895be4ed5ef 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -21,6 +21,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.
+
*** 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 67fd1a9a615..a8eb6a149ce 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -43304,6 +43304,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 @var{;}-separated list of remote stub responses, each
+with a @var{,}-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="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="begin" end="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
@@ -44642,6 +44679,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
@@ -47841,6 +47886,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
@@ -47866,16 +47918,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
+GDB 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:
@@ -47894,16 +47951,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 f55071c8e90..473b8eaa719 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 2c8e1b951ae..bd6d9ef6866 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -369,6 +369,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,
@@ -1140,6 +1146,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,
@@ -5830,6 +5839,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;
@@ -5944,6 +5957,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
@@ -15519,6 +15540,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
@@ -16273,6 +16348,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 25a310244d2..33fbdbc3aaa 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 }
};
@@ -476,6 +506,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,
@@ -492,4 +548,5 @@ const solib_ops solib_target_so_ops =
nullptr,
nullptr,
gdb_bfd_open_from_target_memory,
+ solib_target_ack_library,
};
diff --git a/gdb/solib.c b/gdb/solib.c
index d2520d1be5b..c0cb307bc0f 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -1014,6 +1014,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 ()))
{
@@ -1036,14 +1037,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);
@@ -1729,6 +1739,16 @@ remove_user_added_objfile (struct objfile *objfile)
}
}
+/* 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 0021f5001aa..54d9a0353a5 100644
--- a/gdb/solist.h
+++ b/gdb/solist.h
@@ -178,6 +178,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. */
@@ -197,4 +201,7 @@ 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);
+/* Acknowledge a library. */
+extern void solib_ack_library (solib &so);
+
#endif
diff --git a/gdb/target-delegates.c b/gdb/target-delegates.c
index dd20e1404c3..d3f1a7cc313 100644
--- a/gdb/target-delegates.c
+++ b/gdb/target-delegates.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 1b5aa11ed6f..dc9cbfb8d7b 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -4200,6 +4200,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 81de4a678c3..b840ea05ac2 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -1312,6 +1312,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. */
@@ -2613,4 +2628,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 b00820b8405..25b12de553d 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;
}
@@ -154,6 +242,10 @@ clear_dlls (void)
{
for_each_process ([] (process_info *proc)
{
+ for (dll_info &dll : proc->all_dlls)
+ if (dll.need_ack)
+ ack_dll (proc, dll);
+
proc->all_dlls.clear ();
});
}
diff --git a/gdbserver/dll.h b/gdbserver/dll.h
index 051c30ff8ac..7c06167cf44 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,18 +46,26 @@ struct dll_info
CORE_ADDR begin;
CORE_ADDR end;
CORE_ADDR base_addr;
+ bool need_ack;
};
extern void clear_dlls (void);
-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 15b8aba2281..94c242454d9 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -1903,25 +1903,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);
@@ -1954,6 +1962,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);
@@ -2794,6 +2814,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. */
@@ -2924,6 +2948,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 ();
@@ -3311,6 +3338,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
@@ -3657,6 +3744,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 fd75ee6e7a4..088605ae51d 100644
--- a/gdbserver/server.h
+++ b/gdbserver/server.h
@@ -200,6 +200,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 6db32da2e95..b07a4405c01 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 3643b9110da..2fa88e09ffb 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] 77+ messages in thread
* [PATCH 12/46] gdb, solib, ze: solib_bfd_open_from_target_memory
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (10 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 11/46] gdb, gdbserver, rsp, ze: acknowledge libraries Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 13/46] gdb, remote, ze: fix "$Hc-1#09...Packet received: E01" during startup Tankut Baris Aktemur
` (34 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
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 33fbdbc3aaa..c77353147d1 100644
--- a/gdb/solib-target.c
+++ b/gdb/solib-target.c
@@ -547,6 +547,6 @@ const solib_ops solib_target_so_ops =
nullptr,
nullptr,
nullptr,
- 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 c0cb307bc0f..a7e3633bb3e 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -436,13 +436,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
@@ -460,18 +481,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 54d9a0353a5..6f8372228ef 100644
--- a/gdb/solist.h
+++ b/gdb/solist.h
@@ -201,6 +201,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);
+
/* Acknowledge a library. */
extern void solib_ack_library (solib &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] 77+ messages in thread
* [PATCH 13/46] gdb, remote, ze: fix "$Hc-1#09...Packet received: E01" during startup
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (11 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 12/46] gdb, solib, ze: solib_bfd_open_from_target_memory Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 14/46] gdb, infrun, ze: allow saving process events Tankut Baris Aktemur
` (33 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
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 bd6d9ef6866..7746abd1be2 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -5272,7 +5272,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] 77+ messages in thread
* [PATCH 14/46] gdb, infrun, ze: allow saving process events
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (12 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 13/46] gdb, remote, ze: fix "$Hc-1#09...Packet received: E01" during startup Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-08-31 18:00 ` Lancelot SIX
2024-07-02 11:42 ` [PATCH 15/46] gdb, ze: add TARGET_WAITKIND_UNAVAILABLE Tankut Baris Aktemur
` (32 subsequent siblings)
46 siblings, 1 reply; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
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 | 6 ++++++
gdb/infrun.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 58 insertions(+)
diff --git a/gdb/inferior.h b/gdb/inferior.h
index c08261bdcd3..301594a1558 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -319,6 +319,12 @@ struct inferior_control_state
/* See the definition of stop_kind above. */
enum stop_kind stop_soon;
+
+ /* The waitstatus for this inferior's last event. */
+ struct target_waitstatus waitstatus {};
+
+ /* If true WAITSTATUS hasn't been handled yet. */
+ bool waitstatus_pending_p = false;
};
/* Initialize the inferior-related global state. */
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 1f32a63ad54..1133ef3492f 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_pending_p)
+ {
+ 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_pending_p)
+ {
+ /* 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_pending_p = false;
+
+ 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,23 @@ 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;
+ inf->control.waitstatus_pending_p = true;
+ }
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] 77+ messages in thread
* [PATCH 15/46] gdb, ze: add TARGET_WAITKIND_UNAVAILABLE
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (13 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 14/46] gdb, infrun, ze: allow saving process events Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-09-04 10:47 ` Lancelot SIX
2024-07-02 11:42 ` [PATCH 16/46] gdb, infrun, ze: handle stopping unavailable threads Tankut Baris Aktemur
` (31 subsequent siblings)
46 siblings, 1 reply; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
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 539b11695d9..80372d023da 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 73f6895fe46..91496c594e4 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 1133ef3492f..cc80a4f7f96 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);
@@ -6625,6 +6637,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 41765b102bc..d70a3d616ca 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 7746abd1be2..25584db5b5a 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -4962,7 +4962,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 9e9b5633b12..1cd0eee2236 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 7d5ad3f9776..3d1d1b918c7 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 4ee46936861..71dfb0d0fb1 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -970,7 +970,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] 77+ messages in thread
* [PATCH 16/46] gdb, infrun, ze: handle stopping unavailable threads
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (14 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 15/46] gdb, ze: add TARGET_WAITKIND_UNAVAILABLE Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 17/46] gdb, infrun, ze: allow resuming " Tankut Baris Aktemur
` (30 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
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 cc80a4f7f96..02728457517 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -5611,7 +5611,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)",
@@ -6238,7 +6238,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] 77+ messages in thread
* [PATCH 17/46] gdb, infrun, ze: allow resuming unavailable threads
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (15 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 16/46] gdb, infrun, ze: handle stopping unavailable threads Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 18/46] gdb, gdbserver, ze: add U stop reply Tankut Baris Aktemur
` (29 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
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 02728457517..6e9d1a1792b 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 35f7dc13896..daa714beded 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] 77+ messages in thread
* [PATCH 18/46] gdb, gdbserver, ze: add U stop reply
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (16 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 17/46] gdb, infrun, ze: allow resuming " Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-07-02 13:29 ` Eli Zaretskii
2024-07-02 11:42 ` [PATCH 19/46] gdb, gdbserver, ze: add library notification to " Tankut Baris Aktemur
` (28 subsequent siblings)
46 siblings, 1 reply; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
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).
---
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 895be4ed5ef..fae232e910e 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -28,6 +28,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.
+
*** 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 a8eb6a149ce..c19f5ec82ca 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -24630,6 +24630,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
@@ -43761,6 +43765,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
@@ -44975,6 +44990,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:
@@ -45215,6 +45235,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 25584db5b5a..9aebcbf4052 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -397,6 +397,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
};
@@ -5843,6 +5846,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,
@@ -5958,6 +5963,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+");
@@ -8344,6 +8353,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;
}
}
@@ -8750,7 +8763,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;
@@ -16376,6 +16389,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 5a8eb9ae52a..b7caf0151f5 100644
--- a/gdbserver/remote-utils.cc
+++ b/gdbserver/remote-utils.cc
@@ -1280,6 +1280,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 94c242454d9..36c26fd947a 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;
@@ -2812,6 +2815,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+")
@@ -3441,6 +3450,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
@@ -5124,6 +5143,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] 77+ messages in thread
* [PATCH 19/46] gdb, gdbserver, ze: add library notification to U stop reply
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (17 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 18/46] gdb, gdbserver, ze: add U stop reply Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-07-02 13:30 ` Eli Zaretskii
2024-07-02 11:42 ` [PATCH 20/46] gdbserver, ze: report TARGET_WAITKIND_UNAVAILABLE events Tankut Baris Aktemur
` (27 subsequent siblings)
46 siblings, 1 reply; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
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.
---
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 c19f5ec82ca..9e4c933ea3c 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -43766,10 +43766,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 9aebcbf4052..33298472b6c 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -8354,8 +8354,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 b7caf0151f5..4c76730736d 100644
--- a/gdbserver/remote-utils.cc
+++ b/gdbserver/remote-utils.cc
@@ -1281,9 +1281,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] 77+ messages in thread
* [PATCH 20/46] gdbserver, ze: report TARGET_WAITKIND_UNAVAILABLE events
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (18 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 19/46] gdb, gdbserver, ze: add library notification to " Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 21/46] gdb, ze: handle TARGET_WAITKIND_UNAVAILABLE in stop_all_threads Tankut Baris Aktemur
` (26 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
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 36c26fd947a..ed13b42c1a9 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -5145,12 +5145,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] 77+ messages in thread
* [PATCH 21/46] gdb, ze: handle TARGET_WAITKIND_UNAVAILABLE in stop_all_threads
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (19 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 20/46] gdbserver, ze: report TARGET_WAITKIND_UNAVAILABLE events Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 22/46] gdb, remote: handle thread unavailability in print_one_stopped_thread Tankut Baris Aktemur
` (25 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
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 6e9d1a1792b..a58c5b00aed 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -5579,8 +5579,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] 77+ messages in thread
* [PATCH 22/46] gdb, remote: handle thread unavailability in print_one_stopped_thread
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (20 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 21/46] gdb, ze: handle TARGET_WAITKIND_UNAVAILABLE in stop_all_threads Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 23/46] gdb, remote: do 'remote_add_inferior' in 'remote_notice_new_inferior' earlier Tankut Baris Aktemur
` (24 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
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 33298472b6c..7fe2710c576 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -4869,23 +4869,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] 77+ messages in thread
* [PATCH 23/46] gdb, remote: do 'remote_add_inferior' in 'remote_notice_new_inferior' earlier
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (21 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 22/46] gdb, remote: handle thread unavailability in print_one_stopped_thread Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 24/46] gdb, remote: handle a generic process PID in remote_notice_new_inferior Tankut Baris Aktemur
` (23 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
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 7fe2710c576..8df2e627b66 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -2968,6 +2968,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 ())
{
@@ -2998,18 +3009,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] 77+ messages in thread
* [PATCH 24/46] gdb, remote: handle a generic process PID in remote_notice_new_inferior
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (22 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 23/46] gdb, remote: do 'remote_add_inferior' in 'remote_notice_new_inferior' earlier Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 25/46] gdb, remote: handle a generic process PID in process_stop_reply Tankut Baris Aktemur
` (22 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
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 8df2e627b66..05211587348 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -2979,6 +2979,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] 77+ messages in thread
* [PATCH 25/46] gdb, remote: handle a generic process PID in process_stop_reply
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (23 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 24/46] gdb, remote: handle a generic process PID in remote_notice_new_inferior Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 26/46] gdb: revise the pid_to_exec_file target op Tankut Baris Aktemur
` (21 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
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 05211587348..0864eeb3295 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -8614,18 +8614,23 @@ remote_target::process_stop_reply (stop_reply_up stop_reply,
regcache->raw_supply (reg.num, reg.data.get ());
}
- 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] 77+ messages in thread
* [PATCH 26/46] gdb: revise the pid_to_exec_file target op
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (24 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 25/46] gdb, remote: handle a generic process PID in process_stop_reply Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 27/46] gdb: use the pid from inferior in setup_inferior Tankut Baris Aktemur
` (20 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
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 63eee4297bc..1b43d9ea976 100644
--- a/gdb/exec.c
+++ b/gdb/exec.c
@@ -328,6 +328,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 b840ea05ac2..ea484ca4065 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -1996,6 +1996,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] 77+ messages in thread
* [PATCH 27/46] gdb: use the pid from inferior in setup_inferior
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (25 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 26/46] gdb: revise the pid_to_exec_file target op Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 28/46] gdb: load solibs even when exec_bfd does not exist Tankut Baris Aktemur
` (19 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
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 71514d5ba66..31e60c5b22f 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -2498,7 +2498,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 ();
@@ -2506,7 +2506,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] 77+ messages in thread
* [PATCH 28/46] gdb: load solibs even when exec_bfd does not exist
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (26 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 27/46] gdb: use the pid from inferior in setup_inferior Tankut Baris Aktemur
@ 2024-07-02 11:42 ` Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 29/46] gdbserver: import AC_LIB_HAVE_LINKFLAGS macro into the autoconf script Tankut Baris Aktemur
` (18 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:42 UTC (permalink / raw)
To: gdb-patches
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.
I think the null-check for exec_bfd was simply a guard to avoid
uncessary operations rather than a precondition for having solibs.
So, removing the check should still be safe for existing targets.
This patch is viewed better with the "-w" flag that ignores whitespace
changes.
---
gdb/infcmd.c | 53 ++++++++++++++++++++++++++--------------------------
1 file changed, 26 insertions(+), 27 deletions(-)
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 31e60c5b22f..0297d5a565a 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -260,36 +260,35 @@ post_create_inferior (int from_tty)
throw;
}
- if (current_program_space->exec_bfd ())
- {
- const unsigned solib_add_generation
- = current_program_space->solib_add_generation;
+ {
+ const unsigned solib_add_generation
+ = current_program_space->solib_add_generation;
- scoped_restore restore_in_initial_library_scan
- = make_scoped_restore (¤t_inferior ()->in_initial_library_scan,
- true);
+ scoped_restore restore_in_initial_library_scan
+ = make_scoped_restore (¤t_inferior ()->in_initial_library_scan,
+ true);
- /* Create the hooks to handle shared library load and unload
- events. */
- solib_create_inferior_hook (from_tty);
+ /* Create the hooks to handle shared library load and unload
+ events. */
+ solib_create_inferior_hook (from_tty);
- if (current_program_space->solib_add_generation == solib_add_generation)
- {
- /* The platform-specific hook should load initial shared libraries,
- but didn't. FROM_TTY will be incorrectly 0 but such solib
- targets should be fixed anyway. Call it only after the solib
- target has been initialized by solib_create_inferior_hook. */
-
- if (info_verbose)
- warning (_("platform-specific solib_create_inferior_hook did "
- "not load initial shared libraries."));
-
- /* If the solist is global across processes, there's no need to
- refetch it here. */
- if (!gdbarch_has_global_solist (current_inferior ()->arch ()))
- solib_add (nullptr, 0, auto_solib_add);
- }
- }
+ if (current_program_space->solib_add_generation == solib_add_generation)
+ {
+ /* The platform-specific hook should load initial shared libraries,
+ but didn't. FROM_TTY will be incorrectly 0 but such solib
+ targets should be fixed anyway. Call it only after the solib
+ target has been initialized by solib_create_inferior_hook. */
+
+ if (info_verbose)
+ warning (_("platform-specific solib_create_inferior_hook did "
+ "not load initial shared libraries."));
+
+ /* If the solist is global across processes, there's no need to
+ refetch it here. */
+ if (!gdbarch_has_global_solist (current_inferior ()->arch ()))
+ solib_add (nullptr, 0, auto_solib_add);
+ }
+ }
/* If the user sets watchpoints before execution having started,
then she gets software watchpoints, because GDB can't know which
--
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] 77+ messages in thread
* [PATCH 29/46] gdbserver: import AC_LIB_HAVE_LINKFLAGS macro into the autoconf script
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (27 preceding siblings ...)
2024-07-02 11:42 ` [PATCH 28/46] gdb: load solibs even when exec_bfd does not exist Tankut Baris Aktemur
@ 2024-07-02 11:43 ` Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 30/46] gdbserver: add a pointer to the owner thread in regcache Tankut Baris Aktemur
` (17 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:43 UTC (permalink / raw)
To: gdb-patches
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 5a00c6e9878..44dae81e138 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] 77+ messages in thread
* [PATCH 30/46] gdbserver: add a pointer to the owner thread in regcache
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (28 preceding siblings ...)
2024-07-02 11:43 ` [PATCH 29/46] gdbserver: import AC_LIB_HAVE_LINKFLAGS macro into the autoconf script Tankut Baris Aktemur
@ 2024-07-02 11:43 ` Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 31/46] gdbserver: dump 'xx...x' in collect_register_as_string for unavailable register Tankut Baris Aktemur
` (16 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:43 UTC (permalink / raw)
To: gdb-patches
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 1bb71d10328..0bc9a2efd17 100644
--- a/gdbserver/regcache.cc
+++ b/gdbserver/regcache.cc
@@ -46,6 +46,7 @@ get_thread_regcache (struct thread_info *thread, int fetch)
regcache = new_register_cache (proc->tdesc);
set_thread_regcache_data (thread, regcache);
+ regcache->thread = thread;
}
if (fetch && regcache->registers_valid == 0)
diff --git a/gdbserver/regcache.h b/gdbserver/regcache.h
index 1752c3979d3..ab760a9e551 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] 77+ messages in thread
* [PATCH 31/46] gdbserver: dump 'xx...x' in collect_register_as_string for unavailable register
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (29 preceding siblings ...)
2024-07-02 11:43 ` [PATCH 30/46] gdbserver: add a pointer to the owner thread in regcache Tankut Baris Aktemur
@ 2024-07-02 11:43 ` Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 32/46] gdbserver: wait for stopped threads in queue_stop_reply_callback Tankut Baris Aktemur
` (15 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:43 UTC (permalink / raw)
To: gdb-patches
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 0bc9a2efd17..1b4ed174dff 100644
--- a/gdbserver/regcache.cc
+++ b/gdbserver/regcache.cc
@@ -212,24 +212,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
@@ -484,7 +473,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] 77+ messages in thread
* [PATCH 32/46] gdbserver: wait for stopped threads in queue_stop_reply_callback
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (30 preceding siblings ...)
2024-07-02 11:43 ` [PATCH 31/46] gdbserver: dump 'xx...x' in collect_register_as_string for unavailable register Tankut Baris Aktemur
@ 2024-07-02 11:43 ` Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 33/46] gdbserver: adjust pid after the target attaches Tankut Baris Aktemur
` (14 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:43 UTC (permalink / raw)
To: gdb-patches
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 ed13b42c1a9..6794ba78bf2 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -3836,6 +3836,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] 77+ messages in thread
* [PATCH 33/46] gdbserver: adjust pid after the target attaches
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (31 preceding siblings ...)
2024-07-02 11:43 ` [PATCH 32/46] gdbserver: wait for stopped threads in queue_stop_reply_callback Tankut Baris Aktemur
@ 2024-07-02 11:43 ` Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 34/46] gdb: do not create a thread after a process event Tankut Baris Aktemur
` (13 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:43 UTC (permalink / raw)
To: gdb-patches
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 266c7de8fb8..21ba0e47746 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -1158,7 +1158,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;
struct thread_info *initial_thread;
@@ -1177,7 +1177,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);
@@ -1241,7 +1241,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 273c04626b8..ac8934afce6 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 4b58826e091..4b3711f937a 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 53200ddffc4..00a0287384a 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 6794ba78bf2..0a475dbc8ee 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 2fa88e09ffb..93e1f066955 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 41eed201c10..0e771d015de 100644
--- a/gdbserver/win32-low.cc
+++ b/gdbserver/win32-low.cc
@@ -604,7 +604,7 @@ win32_process_target::create_inferior (const char *program,
PID is the process ID to attach to, specified by the user
or a higher layer. */
int
-win32_process_target::attach (unsigned long pid)
+win32_process_target::attach (int pid)
{
HANDLE h;
DWORD err;
@@ -619,7 +619,7 @@ win32_process_target::attach (unsigned long pid)
/* win32_wait needs to know we're attaching. */
windows_process.attaching = 1;
do_initial_child_stuff (h, pid, 1);
- return 0;
+ return pid;
}
CloseHandle (h);
diff --git a/gdbserver/win32-low.h b/gdbserver/win32-low.h
index ff997df0a66..882db7be434 100644
--- a/gdbserver/win32-low.h
+++ b/gdbserver/win32-low.h
@@ -96,7 +96,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] 77+ messages in thread
* [PATCH 34/46] gdb: do not create a thread after a process event.
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (32 preceding siblings ...)
2024-07-02 11:43 ` [PATCH 33/46] gdbserver: adjust pid after the target attaches Tankut Baris Aktemur
@ 2024-07-02 11:43 ` Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 35/46] gdb, ze: on a whole process stop, mark all threads as not_resumed Tankut Baris Aktemur
` (12 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:43 UTC (permalink / raw)
To: gdb-patches
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 a58c5b00aed..10f63a71c3f 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -6194,7 +6194,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] 77+ messages in thread
* [PATCH 35/46] gdb, ze: on a whole process stop, mark all threads as not_resumed
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (33 preceding siblings ...)
2024-07-02 11:43 ` [PATCH 34/46] gdb: do not create a thread after a process event Tankut Baris Aktemur
@ 2024-07-02 11:43 ` Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 36/46] gdb, dwarf, ze: add DW_OP_INTEL_regval_bits Tankut Baris Aktemur
` (11 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:43 UTC (permalink / raw)
To: gdb-patches
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 0864eeb3295..ce048c40a57 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -8630,11 +8630,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] 77+ messages in thread
* [PATCH 36/46] gdb, dwarf, ze: add DW_OP_INTEL_regval_bits
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (34 preceding siblings ...)
2024-07-02 11:43 ` [PATCH 35/46] gdb, ze: on a whole process stop, mark all threads as not_resumed Tankut Baris Aktemur
@ 2024-07-02 11:43 ` Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 37/46] gdbserver, ze, intelgt: introduce ze-low and intel-ze-low targets Tankut Baris Aktemur
` (10 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:43 UTC (permalink / raw)
To: gdb-patches
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.
---
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 972bb920161..6160d455ef0 100644
--- a/binutils/dwarf.c
+++ b/binutils/dwarf.c
@@ -1704,6 +1704,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 cb80dbf60b1..9896594aa3f 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,
@@ -2329,6 +2351,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 b02cc531640..8ecc4f130da 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 5fea6683575..2f18fe7bfa4 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -2092,6 +2092,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;
@@ -3346,6 +3347,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 66c7fa1220f..4c2e27a155d 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] 77+ messages in thread
* [PATCH 37/46] gdbserver, ze, intelgt: introduce ze-low and intel-ze-low targets
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (35 preceding siblings ...)
2024-07-02 11:43 ` [PATCH 36/46] gdb, dwarf, ze: add DW_OP_INTEL_regval_bits Tankut Baris Aktemur
@ 2024-07-02 11:43 ` Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 38/46] testsuite, sycl: add SYCL support Tankut Baris Aktemur
` (9 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:43 UTC (permalink / raw)
To: gdb-patches
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>
---
config.sub | 6 +-
gdbserver/Makefile.in | 4 +-
gdbserver/config.in | 6 +
gdbserver/configure | 500 ++++++
gdbserver/configure.ac | 18 +
gdbserver/configure.srv | 4 +
gdbserver/intelgt-ze-low.cc | 1039 ++++++++++++
gdbserver/ze-low.cc | 3006 +++++++++++++++++++++++++++++++++++
gdbserver/ze-low.h | 492 ++++++
9 files changed, 5071 insertions(+), 4 deletions(-)
create mode 100644 gdbserver/intelgt-ze-low.cc
create mode 100644 gdbserver/ze-low.cc
create mode 100644 gdbserver/ze-low.h
diff --git a/config.sub b/config.sub
index b4638e4d586..f72256406df 100755
--- a/config.sub
+++ b/config.sub
@@ -4,7 +4,7 @@
# shellcheck disable=SC2006,SC2268 # see below for rationale
-timestamp='2023-09-19'
+timestamp='2024-06-07'
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
@@ -1767,7 +1767,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* )
+ | fiwix* | mlibc* | cos* | mbr* | ze*)
;;
# This one is extra strict with allowed versions
sco3.2v2 | sco3.2v[4-9]* | sco5v6*)
@@ -1820,7 +1820,7 @@ esac
# (given a valid OS), if there is a kernel.
case $kernel-$os-$obj in
linux-gnu*- | linux-dietlibc*- | linux-android*- | linux-newlib*- \
- | linux-musl*- | linux-relibc*- | linux-uclibc*- | linux-mlibc*- )
+ | linux-musl*- | linux-relibc*- | linux-uclibc*- | linux-mlibc*- | linux-ze*- )
;;
uclinux-uclibc*- )
;;
diff --git a/gdbserver/Makefile.in b/gdbserver/Makefile.in
index 6148ccf9121..dcaeb6b7819 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
@@ -373,7 +375,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 47e1e722fe0..db50e49407b 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 3abc647acda..4809fed350f 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
@@ -14739,6 +14747,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 ee0de9decbd..8d307f1a839 100644
--- a/gdbserver/configure.ac
+++ b/gdbserver/configure.ac
@@ -441,6 +441,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 e24e40e85c1..7993c824800 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -139,6 +139,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/intelgt-ze-low.cc b/gdbserver/intelgt-ze-low.cc
new file mode 100644
index 00000000000..819b2c64752
--- /dev/null
+++ b/gdbserver/intelgt-ze-low.cc
@@ -0,0 +1,1039 @@
+/* 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 "server.h"
+#include "ze-low.h"
+
+#include "arch/intelgt.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 ®num,
+ const char *prefix, uint32_t count, const char *group,
+ uint32_t bitsize, const char *type, expedite_t &expedite)
+{
+ for (uint32_t reg = 0; reg < count; ++reg)
+ {
+ std::string name = std::string (prefix) + std::to_string (reg);
+
+ tdesc_create_reg (feature, name.c_str (), regnum++, 1, group,
+ bitsize, type);
+ }
+}
+
+/* Control Register details. */
+
+enum
+ {
+ /* The position of the Breakpoint Suppress bit in CR0.0. */
+ intelgt_cr0_0_breakpoint_suppress = 15,
+
+ /* The position of the Breakpoint Status and Control bit in CR0.1. */
+ intelgt_cr0_1_breakpoint_status = 31,
+
+ /* The position of the External Halt Status and Control bit in CR0.1. */
+ intelgt_cr0_1_external_halt_status = 30,
+
+ /* The position of the Software Exception Control bit in CR0.1. */
+ intelgt_cr0_1_software_exception_control = 29,
+
+ /* The position of the Illegal Opcode Exception Status bit in CR0.1. */
+ intelgt_cr0_1_illegal_opcode_status = 28,
+
+ /* The position of the Force Exception Status and Control bit in CR0.1. */
+ intelgt_cr0_1_force_exception_status = 26,
+
+ /* The position of the Page Fault Status bit in CR0.1.
+ This is a software convention using a reserved bit to indicate
+ page faults by the user mode driver. */
+ intelgt_cr0_1_pagefault_status = 16,
+};
+
+/* 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, unsigned char *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 ®prop,
+ long ®num, ze_regset_info_t ®sets,
+ expedite_t &expedite);
+};
+
+const gdb_byte *
+intelgt_ze_target::sw_breakpoint_from_kind (int kind, int *size)
+{
+ /* We do not support breakpoint instructions.
+
+ Use gdbarch methods that use read/write memory target operations for
+ setting s/w breakopints. */
+ *size = 0;
+ return nullptr;
+}
+
+bool
+intelgt_ze_target::stopped_by_sw_breakpoint ()
+{
+ const ze_thread_info *zetp = ze_thread (current_thread);
+ if (zetp == nullptr)
+ return false;
+
+ ptid_t ptid = ptid_of (current_thread);
+
+ 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> ®set_properties)
+{
+ if (properties.type != ZE_DEVICE_TYPE_GPU)
+ {
+ dprintf ("non-gpu (%x) device (%" PRIx32 "): %s", properties.type,
+ properties.deviceId, properties.name);
+ return false;
+ }
+
+ if (properties.vendorId != 0x8086)
+ {
+ dprintf ("unknown vendor (%" PRIx32 ") of device (%" PRIx32 "): %s",
+ properties.vendorId, properties.deviceId, properties.name);
+ return false;
+ }
+
+ /* We need a few registers to support an Intel GT device.
+
+ Those are registers that GDB itself uses. Without those, we might run into
+ internal errors at some point. We need others, too, that may be referenced
+ in debug information. */
+ bool have_grf = false;
+ bool have_isabase = false;
+ bool have_cr = false;
+ bool have_sr = false;
+ bool have_ce = false;
+ for (const zet_debug_regset_properties_t ®prop : regset_properties)
+ {
+ if (regprop.count < 1)
+ {
+ warning (_("Ignoring empty regset %u 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> ®set_properties,
+ const ze_pci_ext_properties_t &pci_properties)
+{
+ const ze_device_properties_t &properties = dinfo->properties;
+
+ if (properties.vendorId != 0x8086)
+ error (_("unknown vendor (%" PRIx32 ") of device (%" PRIx32 "): %s"),
+ properties.vendorId, properties.deviceId, properties.name);
+
+ target_desc_up tdesc = allocate_target_description ();
+ set_tdesc_architecture (tdesc.get (), "intelgt");
+ set_tdesc_osabi (tdesc.get (), "GNU/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 ®prop : 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 ());
+ 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,
+ unsigned char *buffer)
+{
+ int status = read_memory (tp, pc, buffer, intelgt::MAX_INST_LENGTH);
+ if (status == 0)
+ return intelgt::MAX_INST_LENGTH;
+
+ status = read_memory (tp, pc, buffer, intelgt::COMPACT_INST_LENGTH);
+ if (status > 0)
+ return status;
+
+ if (!intelgt::is_compacted_inst (buffer))
+ return -EIO;
+
+ memset (buffer + 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 = get_thread_process (tp);
+ 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 ®prop,
+ long ®num, ze_regset_info_t ®sets,
+ 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 00000000000..7195f834422
--- /dev/null
+++ b/gdbserver/ze-low.cc
@@ -0,0 +1,3006 @@
+/* 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 "server.h"
+#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 pid_of (device.process);
+
+ 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;
+
+ process_info *process = get_thread_process (thread);
+ return ze_process_device (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)
+{
+ int pid = ze_device_pid (device);
+ for_each_thread (pid, [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;
+
+ 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);
+
+ for_each_thread (pid_of (process), [] (thread_info *thread)
+ {
+ delete (ze_thread_info *) thread->target_data;
+ thread->target_data = nullptr;
+
+ 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. */
+
+ for_each_thread (pid_of (process), [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 ®set : 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 ®set : 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 ®set : 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)
+ {
+ int device_pid = ze_device_pid (*device);
+ for_each_thread (device_pid, [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. */
+ for_each_thread (pid_of (proc), [] (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;
+ for_each_thread (ze_device_pid (device), [&] (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;
+
+ for_each_thread (pid, [&] (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;
+ for (thread_info *tp : all_threads)
+ {
+ if (!tp->id.matches (ptid))
+ continue;
+
+ /* Only consider threads that were resumed. */
+ ze_thread_resume_state_t state = ze_resume_state (tp);
+ if (state == ze_thread_resume_none)
+ continue;
+
+ /* 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)
+ continue;
+
+ if (ze_has_priority_waitstatus (tp))
+ {
+ predicate = ze_has_priority_waitstatus;
+ break;
+ }
+
+ if (is_stopped (tp))
+ predicate = is_stopped;
+ else if ((predicate == nullptr) && ze_has_waitstatus (tp))
+ predicate = ze_has_waitstatus;
+ }
+
+ thread_info *thread = nullptr;
+ if (predicate != nullptr)
+ thread = find_thread_in_random ([ptid, predicate] (thread_info *tp)
+ {
+ if (!tp->id.matches (ptid))
+ return false;
+
+ /* 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 (pid_of (proc)).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 (pid_of (process));
+
+ /* 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 ptid_of (thread);
+ }
+
+ 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);
+
+ int pid = ze_device_pid (*device);
+ for_each_thread (pid, [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 00000000000..e21b85cecb5
--- /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] 77+ messages in thread
* [PATCH 38/46] testsuite, sycl: add SYCL support
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (36 preceding siblings ...)
2024-07-02 11:43 ` [PATCH 37/46] gdbserver, ze, intelgt: introduce ze-low and intel-ze-low targets Tankut Baris Aktemur
@ 2024-07-02 11:43 ` Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 39/46] testsuite, sycl: add test for backtracing inside a kernel Tankut Baris Aktemur
` (8 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:43 UTC (permalink / raw)
To: gdb-patches
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'"
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 | 62 ++++
gdb/testsuite/gdb.sycl/break2.exp | 65 +++++
gdb/testsuite/gdb.sycl/single-task.cpp | 50 ++++
gdb/testsuite/lib/gdb.exp | 29 +-
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 | 382 +++++++++++++++++++++++++
10 files changed, 916 insertions(+), 2 deletions(-)
create mode 100755 gdb/testsuite/boards/intel-offload.exp
create mode 100644 gdb/testsuite/gdb.sycl/break.exp
create mode 100644 gdb/testsuite/gdb.sycl/break2.exp
create mode 100644 gdb/testsuite/gdb.sycl/single-task.cpp
create mode 100644 gdb/testsuite/lib/sycl-devices.cpp
create mode 100644 gdb/testsuite/lib/sycl-hello.cpp
create mode 100644 gdb/testsuite/lib/sycl-util.cpp
create mode 100644 gdb/testsuite/lib/sycl.exp
diff --git a/gdb/testsuite/README b/gdb/testsuite/README
index 06664d723e7..f18f205a050 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 00000000000..5045a7718c9
--- /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 00000000000..2b75dd600ca
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/break.exp
@@ -0,0 +1,62 @@
+# 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 {
+ clean_restart "${binfile}"
+ with_test_prefix [sycl_get_device_prefix $device] {
+ 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 00000000000..6b041aa78d4
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/break2.exp
@@ -0,0 +1,65 @@
+# 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 {
+ clean_restart "${binfile}"
+ with_test_prefix [sycl_get_device_prefix $device] {
+ 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 00000000000..dc6105c0317
--- /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 dfe19c9410d..d2eeae62b5a 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -823,8 +823,18 @@ proc runto { linespec args } {
#
# N.B. This function deletes all existing breakpoints.
# If you don't want that, use gdb_start_cmd.
+#
+# ARGS_LIST is optional input. If it exists,
+# arguments are set before running to the main.
-proc runto_main { } {
+proc runto_main { {args_list {}} } {
+ if {[llength $args_list] > 0} {
+ set args ""
+ foreach arg $args_list {
+ append args "'$arg' "
+ }
+ gdb_test_no_output "set args $args"
+ }
return [runto main qualified]
}
@@ -6150,6 +6160,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
@@ -8394,7 +8419,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/sycl-devices.cpp b/gdb/testsuite/lib/sycl-devices.cpp
new file mode 100644
index 00000000000..ac1ca67faaf
--- /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 00000000000..8de4892ce31
--- /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 00000000000..99e6981d8b2
--- /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 00000000000..edf6f623a9f
--- /dev/null
+++ b/gdb/testsuite/lib/sycl.exp
@@ -0,0 +1,382 @@
+# 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.
+
+# 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
+ 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} {
+ global GDBSERVER_ZE
+
+ 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"
+ }
+
+ if {![runto_main [sycl_get_device_args $device]]} {
+ 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
+ }
+
+ # By default use the "gdbserver-ze" executable. This can be
+ # overridden via the GDBSERVER_ZE parameter to RUNTESTFLAGS.
+ set gdbserverze "gdbserver-ze"
+ if {[info exists GDBSERVER_ZE]} {
+ set gdbserverze $GDBSERVER_ZE
+ verbose -log "SYCL: Using gdbserver-ze: ${gdbserverze}"
+ }
+
+ with_test_prefix "$device" {
+ set inf_pid [get_inferior_pid]
+ gdb_test "add-inferior -no-connection"
+ gdb_test "inferior 2"
+ gdb_test "target remote | $gdbserverze --once --attach - $inf_pid" \
+ "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) Iris(R) Plus Graphics 655 [0x3ea5];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) Iris(R) Plus Graphics 655 [0x3ea5];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:{0x3ea5}
+
+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"
+}
+
+# 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] 77+ messages in thread
* [PATCH 39/46] testsuite, sycl: add test for backtracing inside a kernel
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (37 preceding siblings ...)
2024-07-02 11:43 ` [PATCH 38/46] testsuite, sycl: add SYCL support Tankut Baris Aktemur
@ 2024-07-02 11:43 ` Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 40/46] testsuite, sycl: add test for 'info locals' and 'info args' Tankut Baris Aktemur
` (7 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:43 UTC (permalink / raw)
To: gdb-patches
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 | 189 ++++++++++++++++++++++++++
2 files changed, 281 insertions(+)
create mode 100644 gdb/testsuite/gdb.sycl/call-stack.cpp
create mode 100644 gdb/testsuite/gdb.sycl/call-stack.exp
diff --git a/gdb/testsuite/gdb.sycl/call-stack.cpp b/gdb/testsuite/gdb.sycl/call-stack.cpp
new file mode 100644
index 00000000000..7e6a44f06c7
--- /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 00000000000..a75b3f9eb6a
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/call-stack.exp
@@ -0,0 +1,189 @@
+# 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.
+
+ # Find the address of fourth prologue.
+ set fourth_at_prologue_address "invalid"
+ global hex
+ gdb_test_multiple "info address fourth" \
+ "find prologue of fourth inner function" {
+ -re -wrap "Symbol \"fourth\\(int, int\\)\" is a function at address \(${hex}\).*" {
+ set fourth_at_prologue_address $expect_out(1,string)
+ }
+ }
+ gdb_assert {$fourth_at_prologue_address ne "invalid"}
+
+ gdb_breakpoint "*$fourth_at_prologue_address"
+
+ 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 {
+ clean_restart "${binfile}"
+ with_test_prefix [sycl_get_device_prefix $device] {
+ 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] 77+ messages in thread
* [PATCH 40/46] testsuite, sycl: add test for 'info locals' and 'info args'
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (38 preceding siblings ...)
2024-07-02 11:43 ` [PATCH 39/46] testsuite, sycl: add test for backtracing inside a kernel Tankut Baris Aktemur
@ 2024-07-02 11:43 ` Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 41/46] testsuite, sycl: add tests for stepping and accessing data elements Tankut Baris Aktemur
` (6 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:43 UTC (permalink / raw)
To: gdb-patches
Test that local variables and arguments inside a SYCL kernel can be
displayed correctly.
---
.../gdb.sycl/info-locals-and-args.exp | 77 +++++++++++++++++++
1 file changed, 77 insertions(+)
create mode 100644 gdb/testsuite/gdb.sycl/info-locals-and-args.exp
diff --git a/gdb/testsuite/gdb.sycl/info-locals-and-args.exp b/gdb/testsuite/gdb.sycl/info-locals-and-args.exp
new file mode 100644
index 00000000000..e56a569e63f
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/info-locals-and-args.exp
@@ -0,0 +1,77 @@
+# 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 {
+ clean_restart "${binfile}"
+ with_test_prefix [sycl_get_device_prefix $device] {
+ 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] 77+ messages in thread
* [PATCH 41/46] testsuite, sycl: add tests for stepping and accessing data elements
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (39 preceding siblings ...)
2024-07-02 11:43 ` [PATCH 40/46] testsuite, sycl: add test for 'info locals' and 'info args' Tankut Baris Aktemur
@ 2024-07-02 11:43 ` Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 42/46] testsuite, sycl: add test for 1-D and 2-D parallel_for kernels Tankut Baris Aktemur
` (5 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:43 UTC (permalink / raw)
To: gdb-patches
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 | 46 +++++++++++++++++
gdb/testsuite/gdb.sycl/step.exp | 50 +++++++++++++++++++
2 files changed, 96 insertions(+)
create mode 100644 gdb/testsuite/gdb.sycl/step-into-function.exp
create mode 100644 gdb/testsuite/gdb.sycl/step.exp
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 00000000000..d1552093f62
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/step-into-function.exp
@@ -0,0 +1,46 @@
+# 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 {
+ clean_restart "${binfile}"
+ with_test_prefix [sycl_get_device_prefix $device] {
+ 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 00000000000..ac29f667ca4
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/step.exp
@@ -0,0 +1,50 @@
+# 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 {
+ clean_restart "${binfile}"
+ with_test_prefix [sycl_get_device_prefix $device] {
+ 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] 77+ messages in thread
* [PATCH 42/46] testsuite, sycl: add test for 1-D and 2-D parallel_for kernels
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (40 preceding siblings ...)
2024-07-02 11:43 ` [PATCH 41/46] testsuite, sycl: add tests for stepping and accessing data elements Tankut Baris Aktemur
@ 2024-07-02 11:43 ` Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 43/46] testsuite, sycl: add test for scheduler-locking Tankut Baris Aktemur
` (4 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:43 UTC (permalink / raw)
To: gdb-patches
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 | 54 +++++++++++++++
gdb/testsuite/gdb.sycl/parallel-for-2D.cpp | 73 ++++++++++++++++++++
gdb/testsuite/gdb.sycl/parallel-for-2D.exp | 54 +++++++++++++++
gdb/testsuite/gdb.sycl/step-parallel-for.exp | 62 +++++++++++++++++
5 files changed, 315 insertions(+)
create mode 100644 gdb/testsuite/gdb.sycl/parallel-for-1D.cpp
create mode 100644 gdb/testsuite/gdb.sycl/parallel-for-1D.exp
create mode 100644 gdb/testsuite/gdb.sycl/parallel-for-2D.cpp
create mode 100644 gdb/testsuite/gdb.sycl/parallel-for-2D.exp
create mode 100644 gdb/testsuite/gdb.sycl/step-parallel-for.exp
diff --git a/gdb/testsuite/gdb.sycl/parallel-for-1D.cpp b/gdb/testsuite/gdb.sycl/parallel-for-1D.cpp
new file mode 100644
index 00000000000..bbcff7253b6
--- /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 00000000000..56ea6b421f0
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/parallel-for-1D.exp
@@ -0,0 +1,54 @@
+# 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 {
+ clean_restart "${binfile}"
+ with_test_prefix [sycl_get_device_prefix $device] {
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ set last_line [gdb_get_line_number "kernel-last-line"]
+ gdb_breakpoint $last_line
+
+ # Check that we hit the BP for a number of elements. We do not check
+ # for each element because the number of hits received may depend on
+ # whether the kernel was vectorized, and if so, the width of vectors.
+ # Since the data array in the test program is large, having a small
+ # number of trips here should be safe.
+
+ for {set i 1} {$i <= 5} {incr i} { with_test_prefix "trip $i" {
+ gdb_continue_to_breakpoint "hit the last line" \
+ ".*$srcfile:$last_line.*"
+ }}
+ }
+}
diff --git a/gdb/testsuite/gdb.sycl/parallel-for-2D.cpp b/gdb/testsuite/gdb.sycl/parallel-for-2D.cpp
new file mode 100644
index 00000000000..0607f8ffd25
--- /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 00000000000..3457fe7d657
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/parallel-for-2D.exp
@@ -0,0 +1,54 @@
+# 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 {
+ clean_restart "${binfile}"
+ with_test_prefix [sycl_get_device_prefix $device] {
+ 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 00000000000..4b62d869991
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/step-parallel-for.exp
@@ -0,0 +1,62 @@
+# 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 {
+ clean_restart "${binfile}"
+ with_test_prefix [sycl_get_device_prefix $device] {
+ 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] 77+ messages in thread
* [PATCH 43/46] testsuite, sycl: add test for scheduler-locking
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (41 preceding siblings ...)
2024-07-02 11:43 ` [PATCH 42/46] testsuite, sycl: add test for 1-D and 2-D parallel_for kernels Tankut Baris Aktemur
@ 2024-07-02 11:43 ` Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 44/46] testsuite, arch, intelgt: add a disassembly test Tankut Baris Aktemur
` (3 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:43 UTC (permalink / raw)
To: gdb-patches
Add a new test to check that scheduler-locking when inside a SYCL
kernel works fine.
---
gdb/testsuite/gdb.sycl/scheduler-locking.exp | 66 ++++++++++++++++++++
1 file changed, 66 insertions(+)
create mode 100644 gdb/testsuite/gdb.sycl/scheduler-locking.exp
diff --git a/gdb/testsuite/gdb.sycl/scheduler-locking.exp b/gdb/testsuite/gdb.sycl/scheduler-locking.exp
new file mode 100644
index 00000000000..7aa1f4e8f74
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/scheduler-locking.exp
@@ -0,0 +1,66 @@
+# 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 {
+ clean_restart "${binfile}"
+ with_test_prefix [sycl_get_device_prefix $device] {
+ 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] 77+ messages in thread
* [PATCH 44/46] testsuite, arch, intelgt: add a disassembly test
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (42 preceding siblings ...)
2024-07-02 11:43 ` [PATCH 43/46] testsuite, sycl: add test for scheduler-locking Tankut Baris Aktemur
@ 2024-07-02 11:43 ` Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 45/46] testsuite, arch, intelgt: add intelgt-program-bp.exp Tankut Baris Aktemur
` (2 subsequent siblings)
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:43 UTC (permalink / raw)
To: gdb-patches
From: Albertano Caruso <albertano.caruso@intel.com>
Test the disassemble command on the Intel GT target.
---
.../gdb.arch/intelgt-disassemble.exp | 83 +++++++++++++++++++
gdb/testsuite/gdb.arch/sycl-simple.cpp | 42 ++++++++++
2 files changed, 125 insertions(+)
create mode 100644 gdb/testsuite/gdb.arch/intelgt-disassemble.exp
create mode 100644 gdb/testsuite/gdb.arch/sycl-simple.cpp
diff --git a/gdb/testsuite/gdb.arch/intelgt-disassemble.exp b/gdb/testsuite/gdb.arch/intelgt-disassemble.exp
new file mode 100644
index 00000000000..2246504d9aa
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/intelgt-disassemble.exp
@@ -0,0 +1,83 @@
+# 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 {
+ clean_restart "${binfile}"
+ with_test_prefix [sycl_get_device_prefix $device] {
+ if {![require_sycl_device "$device" "gpu" "Intel*"]} {
+ unsupported "test is aimed at Intel GPUs only"
+ continue
+ }
+
+ 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 with libiga"
+
+ untested "disassemble without libiga"
+ } else {
+ untested "disassemble with libiga"
+
+ 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.*"] \
+ "disassemble without libiga"
+ }
+ }
+}
diff --git a/gdb/testsuite/gdb.arch/sycl-simple.cpp b/gdb/testsuite/gdb.arch/sycl-simple.cpp
new file mode 100644
index 00000000000..a51fd1ac3fd
--- /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] 77+ messages in thread
* [PATCH 45/46] testsuite, arch, intelgt: add intelgt-program-bp.exp
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (43 preceding siblings ...)
2024-07-02 11:43 ` [PATCH 44/46] testsuite, arch, intelgt: add a disassembly test Tankut Baris Aktemur
@ 2024-07-02 11:43 ` Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 46/46] testsuite, sycl: test canceling a stepping flow Tankut Baris Aktemur
2024-08-23 6:54 ` [PATCH 00/46] A new target to debug Intel GPUs Aktemur, Tankut Baris
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:43 UTC (permalink / raw)
To: gdb-patches
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 | 104 ++++++++++++++++++
1 file changed, 104 insertions(+)
create mode 100644 gdb/testsuite/gdb.arch/intelgt-program-bp.exp
diff --git a/gdb/testsuite/gdb.arch/intelgt-program-bp.exp b/gdb/testsuite/gdb.arch/intelgt-program-bp.exp
new file mode 100644
index 00000000000..29f1c21baf6
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/intelgt-program-bp.exp
@@ -0,0 +1,104 @@
+# 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
+}
+
+# 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
+ }
+}
+
+foreach device $sycl_device_list {
+ clean_restart "${binfile}"
+ with_test_prefix [sycl_get_device_prefix $device] {
+ if {![require_sycl_device "$device" "gpu" "Intel*"]} {
+ unsupported "test is aimed at Intel GPUs only"
+ continue
+ }
+
+ 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] 77+ messages in thread
* [PATCH 46/46] testsuite, sycl: test canceling a stepping flow
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (44 preceding siblings ...)
2024-07-02 11:43 ` [PATCH 45/46] testsuite, arch, intelgt: add intelgt-program-bp.exp Tankut Baris Aktemur
@ 2024-07-02 11:43 ` Tankut Baris Aktemur
2024-08-23 6:54 ` [PATCH 00/46] A new target to debug Intel GPUs Aktemur, Tankut Baris
46 siblings, 0 replies; 77+ messages in thread
From: Tankut Baris Aktemur @ 2024-07-02 11:43 UTC (permalink / raw)
To: gdb-patches
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 | 85 ++++++++++++++++++++++++
1 file changed, 85 insertions(+)
create mode 100644 gdb/testsuite/gdb.sycl/step-canceled.exp
diff --git a/gdb/testsuite/gdb.sycl/step-canceled.exp b/gdb/testsuite/gdb.sycl/step-canceled.exp
new file mode 100644
index 00000000000..e30e44396af
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/step-canceled.exp
@@ -0,0 +1,85 @@
+# 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 {
+ clean_restart "${binfile}"
+ with_test_prefix [sycl_get_device_prefix $device] {
+ 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] 77+ messages in thread
* Re: [PATCH 10/46] gdb, gdbserver, ze: in-memory libraries
2024-07-02 11:42 ` [PATCH 10/46] gdb, gdbserver, ze: in-memory libraries Tankut Baris Aktemur
@ 2024-07-02 13:20 ` Eli Zaretskii
0 siblings, 0 replies; 77+ messages in thread
From: Eli Zaretskii @ 2024-07-02 13:20 UTC (permalink / raw)
To: Tankut Baris Aktemur; +Cc: gdb-patches
> From: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
> Date: Tue, 2 Jul 2024 13:42:41 +0200
>
> 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.
> ---
> gdb/NEWS | 8 +++
> gdb/doc/gdb.texinfo | 40 +++++++++----
> gdb/features/library-list.dtd | 8 ++-
> gdb/remote.c | 3 +
> gdb/solib-target.c | 90 +++++++++++++++++++++++++++--
> 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, 348 insertions(+), 28 deletions(-)
Thanks, the documentation parts are approved.
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH 11/46] gdb, gdbserver, rsp, ze: acknowledge libraries
2024-07-02 11:42 ` [PATCH 11/46] gdb, gdbserver, rsp, ze: acknowledge libraries Tankut Baris Aktemur
@ 2024-07-02 13:25 ` Eli Zaretskii
2024-07-03 7:19 ` Aktemur, Tankut Baris
0 siblings, 1 reply; 77+ messages in thread
From: Eli Zaretskii @ 2024-07-02 13:25 UTC (permalink / raw)
To: Tankut Baris Aktemur; +Cc: gdb-patches
> From: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
> Date: Tue, 2 Jul 2024 13:42:42 +0200
>
> 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.c | 50 ++++++++++++++++
> gdb/target.c | 16 +++++
> gdb/target.h | 21 +++++++
> gdbserver/dll.cc | 104 +++++++++++++++++++++++++++++++--
> gdbserver/dll.h | 25 +++++---
> gdbserver/server.cc | 107 +++++++++++++++++++++++++++++++---
> gdbserver/server.h | 4 ++
> gdbserver/target.cc | 14 +++++
> gdbserver/target.h | 20 +++++++
> 16 files changed, 589 insertions(+), 35 deletions(-)
Thanks.
> diff --git a/gdb/NEWS b/gdb/NEWS
> index f0c70d69bb6..895be4ed5ef 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -21,6 +21,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.
> +
> *** Changes in GDB 15
This part is okay.
> +@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 @var{;}-separated list of remote stub responses, each
^^^^^^^
This should use @samp, not @var.
> +with a @var{,}-separated list of arguments defined by its @var{type}.
^^^^^^^
Likewise here (only the first instance of @var).
> +@item library:@var{name}
> +Acknowledge the shared library that had been reported as
> +@samp{<library name="name" ack="yes">} in the remote stub's
^^^^^^
This should be "@var{name}"
> +@item in-memory-library:@var{begin},@var{end}
> +Acknowledge the shared library that had been reported as
> +@samp{<in-memory-library begin="begin" end="end" ack="yes">} in the
Likewise here for "begin" and "end" -- they are not literal strings.
> +GDB will acknowledge the library with a @samp{vAck;library} or, as in
^^^
@value{GDBN}
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH 07/46] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description
2024-07-02 11:42 ` [PATCH 07/46] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description Tankut Baris Aktemur
@ 2024-07-02 13:28 ` Eli Zaretskii
0 siblings, 0 replies; 77+ messages in thread
From: Eli Zaretskii @ 2024-07-02 13:28 UTC (permalink / raw)
To: Tankut Baris Aktemur; +Cc: gdb-patches
> From: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
> Date: Tue, 2 Jul 2024 13:42:38 +0200
>
> 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>
> ---
> 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, the documentation parts are okay.
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH 18/46] gdb, gdbserver, ze: add U stop reply
2024-07-02 11:42 ` [PATCH 18/46] gdb, gdbserver, ze: add U stop reply Tankut Baris Aktemur
@ 2024-07-02 13:29 ` Eli Zaretskii
0 siblings, 0 replies; 77+ messages in thread
From: Eli Zaretskii @ 2024-07-02 13:29 UTC (permalink / raw)
To: Tankut Baris Aktemur; +Cc: gdb-patches
> From: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
> Date: Tue, 2 Jul 2024 13:42:49 +0200
>
> 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).
> ---
> 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(-)
Thanks, the documentation parts are approved.
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH 19/46] gdb, gdbserver, ze: add library notification to U stop reply
2024-07-02 11:42 ` [PATCH 19/46] gdb, gdbserver, ze: add library notification to " Tankut Baris Aktemur
@ 2024-07-02 13:30 ` Eli Zaretskii
0 siblings, 0 replies; 77+ messages in thread
From: Eli Zaretskii @ 2024-07-02 13:30 UTC (permalink / raw)
To: Tankut Baris Aktemur; +Cc: gdb-patches
> From: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
> Date: Tue, 2 Jul 2024 13:42:50 +0200
>
> 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.
> ---
> gdb/doc/gdb.texinfo | 5 +++++
> gdb/remote.c | 11 +++++++++--
> gdbserver/remote-utils.cc | 22 +++++++++++++++++++---
> 3 files changed, 33 insertions(+), 5 deletions(-)
Thanks, the documentation part is okay.
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH 01/46] gdb, intelgt: add intelgt as a basic machine
2024-07-02 11:42 ` [PATCH 01/46] gdb, intelgt: add intelgt as a basic machine Tankut Baris Aktemur
@ 2024-07-02 15:49 ` Maciej W. Rozycki
2024-07-03 8:01 ` Aktemur, Tankut Baris
0 siblings, 1 reply; 77+ messages in thread
From: Maciej W. Rozycki @ 2024-07-02 15:49 UTC (permalink / raw)
To: Tankut Baris Aktemur; +Cc: gdb-patches
On Tue, 2 Jul 2024, Tankut Baris Aktemur wrote:
> Add 'intelgt' as a basic machine to config.sub.
This file is maintained elsewhere and changes needs to be submitted to
<config-patches@gnu.org> before they are imported to GDB or binutils; see
the top-level MAINTAINERS file for further details of the process here.
Maciej
^ permalink raw reply [flat|nested] 77+ messages in thread
* RE: [PATCH 11/46] gdb, gdbserver, rsp, ze: acknowledge libraries
2024-07-02 13:25 ` Eli Zaretskii
@ 2024-07-03 7:19 ` Aktemur, Tankut Baris
0 siblings, 0 replies; 77+ messages in thread
From: Aktemur, Tankut Baris @ 2024-07-03 7:19 UTC (permalink / raw)
To: Eli Zaretskii, gdb-patches
On Tuesday, July 2, 2024 3:26 PM, Eli Zaretskii wrote:
> > +@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 @var{;}-separated list of remote stub responses, each
> ^^^^^^^
> This should use @samp, not @var.
>
> > +with a @var{,}-separated list of arguments defined by its @var{type}.
> ^^^^^^^
> Likewise here (only the first instance of @var).
>
> > +@item library:@var{name}
> > +Acknowledge the shared library that had been reported as
> > +@samp{<library name="name" ack="yes">} in the remote stub's
> ^^^^^^
> This should be "@var{name}"
>
> > +@item in-memory-library:@var{begin},@var{end}
> > +Acknowledge the shared library that had been reported as
> > +@samp{<in-memory-library begin="begin" end="end" ack="yes">} in the
>
> Likewise here for "begin" and "end" -- they are not literal strings.
>
> > +GDB will acknowledge the library with a @samp{vAck;library} or, as in
> ^^^
> @value{GDBN}
Thank you. I made all the fixes on my local branch and will include them
in the next revision. I also added the "Reviewed-by:" annotations to the
other patches you reviewed.
-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] 77+ messages in thread
* RE: [PATCH 01/46] gdb, intelgt: add intelgt as a basic machine
2024-07-02 15:49 ` Maciej W. Rozycki
@ 2024-07-03 8:01 ` Aktemur, Tankut Baris
0 siblings, 0 replies; 77+ messages in thread
From: Aktemur, Tankut Baris @ 2024-07-03 8:01 UTC (permalink / raw)
To: Maciej W. Rozycki, gdb-patches
On Tuesday, July 2, 2024 5:50 PM, Maciej W. Rozycki wrote:
> On Tue, 2 Jul 2024, Tankut Baris Aktemur wrote:
>
> > Add 'intelgt' as a basic machine to config.sub.
>
> This file is maintained elsewhere and changes needs to be submitted to
> <config-patches@gnu.org> before they are imported to GDB or binutils; see
> the top-level MAINTAINERS file for further details of the process here.
>
> Maciej
Thank you; I will do so.
There are a few patches in the series that touch binutils, bfd, opcodes, ld,
and include. I'll also submit them to their corresponding lists.
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] 77+ messages in thread
* Re: [PATCH 05/46] gdb, arch, intelgt: add intelgt arch definitions
2024-07-02 11:42 ` [PATCH 05/46] gdb, arch, intelgt: add intelgt arch definitions Tankut Baris Aktemur
@ 2024-07-03 17:17 ` Guinevere Larsen
2024-07-04 6:47 ` Aktemur, Tankut Baris
2024-10-15 22:44 ` Thiago Jung Bauermann
1 sibling, 1 reply; 77+ messages in thread
From: Guinevere Larsen @ 2024-07-03 17:17 UTC (permalink / raw)
To: Tankut Baris Aktemur, gdb-patches
[-- Attachment #1: Type: text/plain, Size: 1153 bytes --]
On 7/2/24 8:42 AM, Tankut Baris Aktemur wrote:
> 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/arch/intelgt.c | 78 ++++++++++++++++++++
> gdb/arch/intelgt.h | 175 +++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 253 insertions(+)
> create mode 100644 gdb/arch/intelgt.c
> create mode 100644 gdb/arch/intelgt.h
I was just taking a quick look and noticed you added the .c and .h
files, but didn't change the Makefile.in here (you add it in the next
patch). shouldn't that change be moved to this patch?
--
Cheers,
Guinevere Larsen
She/Her/Hers
^ permalink raw reply [flat|nested] 77+ messages in thread
* RE: [PATCH 05/46] gdb, arch, intelgt: add intelgt arch definitions
2024-07-03 17:17 ` Guinevere Larsen
@ 2024-07-04 6:47 ` Aktemur, Tankut Baris
0 siblings, 0 replies; 77+ messages in thread
From: Aktemur, Tankut Baris @ 2024-07-04 6:47 UTC (permalink / raw)
To: Guinevere Larsen, gdb-patches; +Cc: Metzger, Markus T
On Wednesday, July 3, 2024 7:17 PM, Guinevere Larsen wrote:
> On 7/2/24 8:42 AM, Tankut Baris Aktemur wrote:
> > From: Markus Metzger mailto: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 mailto:tankut.baris.aktemur@intel.com
> > Co-authored-by: Mihails Strasuns mailto:mihails.strasuns@intel.com
> > Co-authored-by: Natalia Saiapova mailto:natalia.saiapova@intel.com
> > ---
> > gdb/arch/intelgt.c | 78 ++++++++++++++++++++
> > gdb/arch/intelgt.h | 175 +++++++++++++++++++++++++++++++++++++++++++++
> > 2 files changed, 253 insertions(+)
> > create mode 100644 gdb/arch/intelgt.c
> > create mode 100644 gdb/arch/intelgt.h
> I was just taking a quick look and noticed you added the .c and .h files, but didn't change the Makefile.in here (you add it in the next patch). shouldn't that change be moved to this patch?
Thanks for checking. Yes, it makes sense to move it. I updated the
local branch. We will include the change in the next revision.
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] 77+ messages in thread
* RE: [PATCH 00/46] A new target to debug Intel GPUs
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
` (45 preceding siblings ...)
2024-07-02 11:43 ` [PATCH 46/46] testsuite, sycl: test canceling a stepping flow Tankut Baris Aktemur
@ 2024-08-23 6:54 ` Aktemur, Tankut Baris
46 siblings, 0 replies; 77+ messages in thread
From: Aktemur, Tankut Baris @ 2024-08-23 6:54 UTC (permalink / raw)
To: gdb-patches
Kindly pinging.
Regards,
-Baris
> -----Original Message-----
> From: Aktemur, Tankut Baris <tankut.baris.aktemur@intel.com>
> Sent: Tuesday, July 2, 2024 1:43 PM
> To: gdb-patches@sourceware.org
> Subject: [PATCH 00/46] A new target to debug Intel GPUs
>
> 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
>
> GPU threads operate in a SIMD (single instruction multiple data)
> manner: they are vectorized, where each SIMD lane (also known as
> "execution channel") executes the same instruction but using different
> data values. SIMD 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.
>
> 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 in the process
> of being submitted to upstream at
>
> https://gitlab.freedesktop.org/miku/kernel/-/tree/eudebug-dev
>
> 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 [1] 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.
>
> 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)
>
> We also submit patches for the testsuite, where we introduce the
> infrastructure and a number of test cases using SYCL.
>
> Regards,
> Baris
>
> [1]: https://www.khronos.org/sycl/
>
>
> 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 (25):
> 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: revise the pid_to_exec_file target op
> gdb: use the pid from inferior in setup_inferior
> gdb: load solibs even when exec_bfd does not exist
> 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
> 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 | 7 +-
> gdb/Makefile.in | 8 +-
> gdb/NEWS | 29 +
> gdb/arch/intelgt.c | 78 +
> 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 | 57 +-
> gdb/inferior.h | 6 +
> gdb/infrun.c | 125 +-
> gdb/intelgt-tdep.c | 1110 ++++++
> gdb/nat/fork-inferior.c | 10 +
> gdb/regcache.c | 6 +-
> gdb/remote.c | 196 +-
> gdb/solib-target.c | 147 +-
> gdb/solib.c | 103 +-
> gdb/solist.h | 25 +-
> gdb/target-delegates.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.arch/intelgt-disassemble.exp | 83 +
> gdb/testsuite/gdb.arch/intelgt-program-bp.exp | 104 +
> gdb/testsuite/gdb.arch/sycl-simple.cpp | 42 +
> gdb/testsuite/gdb.sycl/break.exp | 62 +
> gdb/testsuite/gdb.sycl/break2.exp | 65 +
> gdb/testsuite/gdb.sycl/call-stack.cpp | 92 +
> gdb/testsuite/gdb.sycl/call-stack.exp | 189 ++
> .../gdb.sycl/info-locals-and-args.exp | 77 +
> gdb/testsuite/gdb.sycl/parallel-for-1D.cpp | 72 +
> gdb/testsuite/gdb.sycl/parallel-for-1D.exp | 54 +
> gdb/testsuite/gdb.sycl/parallel-for-2D.cpp | 73 +
> gdb/testsuite/gdb.sycl/parallel-for-2D.exp | 54 +
> gdb/testsuite/gdb.sycl/scheduler-locking.exp | 66 +
> gdb/testsuite/gdb.sycl/single-task.cpp | 50 +
> gdb/testsuite/gdb.sycl/step-canceled.exp | 85 +
> gdb/testsuite/gdb.sycl/step-into-function.exp | 46 +
> gdb/testsuite/gdb.sycl/step-parallel-for.exp | 62 +
> gdb/testsuite/gdb.sycl/step.exp | 50 +
> gdb/testsuite/gdb.threads/killed-outside.exp | 4 +
> gdb/testsuite/lib/gdb.exp | 29 +-
> 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 | 382 +++
> 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 | 4 +
> gdbserver/dll.cc | 159 +-
> gdbserver/dll.h | 36 +-
> gdbserver/intelgt-ze-low.cc | 1039 ++++++
> 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 | 3006 +++++++++++++++++
> gdbserver/ze-low.h | 492 +++
> gdbsupport/filestuff.cc | 39 +
> 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 +
> 113 files changed, 11243 insertions(+), 167 deletions(-)
> mode change 100644 => 100755 bfd/config.bfd
> create mode 100644 bfd/cpu-intelgt.c
> create mode 100644 bfd/elf64-intelgt.c
> create mode 100644 gdb/arch/intelgt.c
> create mode 100644 gdb/arch/intelgt.h
> create mode 100755 gdb/intelgt-tdep.c
> create mode 100755 gdb/testsuite/boards/intel-offload.exp
> create mode 100644 gdb/testsuite/gdb.arch/intelgt-disassemble.exp
> create mode 100644 gdb/testsuite/gdb.arch/intelgt-program-bp.exp
> create mode 100644 gdb/testsuite/gdb.arch/sycl-simple.cpp
> create mode 100644 gdb/testsuite/gdb.sycl/break.exp
> create mode 100644 gdb/testsuite/gdb.sycl/break2.exp
> create mode 100644 gdb/testsuite/gdb.sycl/call-stack.cpp
> create mode 100644 gdb/testsuite/gdb.sycl/call-stack.exp
> create mode 100644 gdb/testsuite/gdb.sycl/info-locals-and-args.exp
> create mode 100644 gdb/testsuite/gdb.sycl/parallel-for-1D.cpp
> create mode 100644 gdb/testsuite/gdb.sycl/parallel-for-1D.exp
> create mode 100644 gdb/testsuite/gdb.sycl/parallel-for-2D.cpp
> create mode 100644 gdb/testsuite/gdb.sycl/parallel-for-2D.exp
> create mode 100644 gdb/testsuite/gdb.sycl/scheduler-locking.exp
> create mode 100644 gdb/testsuite/gdb.sycl/single-task.cpp
> create mode 100644 gdb/testsuite/gdb.sycl/step-canceled.exp
> create mode 100644 gdb/testsuite/gdb.sycl/step-into-function.exp
> create mode 100644 gdb/testsuite/gdb.sycl/step-parallel-for.exp
> create mode 100644 gdb/testsuite/gdb.sycl/step.exp
> create mode 100644 gdb/testsuite/lib/sycl-devices.cpp
> create mode 100644 gdb/testsuite/lib/sycl-hello.cpp
> create mode 100644 gdb/testsuite/lib/sycl-util.cpp
> create mode 100644 gdb/testsuite/lib/sycl.exp
> create mode 100644 gdbserver/intelgt-ze-low.cc
> create mode 100644 gdbserver/ze-low.cc
> create mode 100644 gdbserver/ze-low.h
> create mode 100644 include/elf/intelgt.h
>
> --
> 2.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] 77+ messages in thread
* Re: [PATCH 06/46] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture
2024-07-02 11:42 ` [PATCH 06/46] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture Tankut Baris Aktemur
@ 2024-08-26 16:27 ` Lancelot SIX
2024-08-29 13:09 ` Aktemur, Tankut Baris
2024-10-15 23:35 ` Thiago Jung Bauermann
1 sibling, 1 reply; 77+ messages in thread
From: Lancelot SIX @ 2024-08-26 16:27 UTC (permalink / raw)
To: Tankut Baris Aktemur, gdb-patches
Hi Baris,
I have included some comments below. Since i am not familiar with the
intelgt architectures, I won’t comment much on the architecture specific
parts.
On 02/07/2024 12:42, Tankut Baris Aktemur wrote:
> 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, IA, 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 | 2 +
> gdb/configure.tgt | 5 +
> gdb/intelgt-tdep.c | 987 +++++++++++++++++++++++++++++++++++++++++++++
> gdb/regcache.c | 6 +-
> 4 files changed, 999 insertions(+), 1 deletion(-)
> create mode 100755 gdb/intelgt-tdep.c
>
> diff --git a/gdb/Makefile.in b/gdb/Makefile.in
> index 1c697bf0ab1..830311100b0 100644
> --- a/gdb/Makefile.in
> +++ b/gdb/Makefile.in
> @@ -750,11 +750,13 @@ 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 \
> 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 8326c458eb1..5764db9f199 100644
> --- a/gdb/configure.tgt
> +++ b/gdb/configure.tgt
> @@ -356,6 +356,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"
The "arch/intelgt.c" file is not included in this patch. Should that
part of the configure.tgt change be in another patch?
> + ;;
> +
> iq2000-*-*)
> gdb_target_obs="iq2000-tdep.o"
> ;;
> diff --git a/gdb/intelgt-tdep.c b/gdb/intelgt-tdep.c
> new file mode 100755
> index 00000000000..83cddec8d4b
> --- /dev/null
> +++ b/gdb/intelgt-tdep.c
> @@ -0,0 +1,987 @@
> +/* 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 "defs.h"
I don’t think this is required anymore. See:
commit 18d2988e5da8919514c76b83e2c0b56e439018bd
gdb, gdbserver, gdbsupport: remove includes of early headers
> +#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;
> +
> +#define dprintf(...) \
> + do \
> + { \
> + if (intelgt_debug) \
> + { \
> + gdb_printf (gdb_stdlog, "%s: ", __func__); \
> + gdb_printf (gdb_stdlog, __VA_ARGS__); \
> + gdb_printf (gdb_stdlog, "\n"); \
> + } \
> + } \
> + while (0)
Could this have been done using "debug_prefixed_printf_cond" (from
gdbsupport/common-debug.h)? This would allow consistent debug outputs
across GDB.
> +
> +/* Regnum pair describing the assigned regnum range for a single
> + regset. */
> +
> +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);
I have not checked yet other patches, is this function going to be used
later, if do does the later patch using it will remove the maybe_unused?
> +
> +/* The 'gdbarch_data' stuff specific for this architecture. */
> +
> +struct intelgt_gdbarch_data
> +{
> + /* $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-register 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_data ()
> + {
> + memset (®set_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;
> + }
> +};
> +
> +static const registry<gdbarch>::key<intelgt_gdbarch_data>
> + intelgt_gdbarch_data_handle;
> +
> +static intelgt_gdbarch_data *
> +get_intelgt_gdbarch_data (gdbarch *gdbarch)
> +{
> + intelgt_gdbarch_data *result = intelgt_gdbarch_data_handle.get (gdbarch);
> + if (result == nullptr)
> + result = intelgt_gdbarch_data_handle.emplace (gdbarch);
> + return result;
> +}
> +
> +/* 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, size_t size, 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 + size > regsize)
> + error (_("%s[%ld:%ld] is outside the range of %s[%d:0]."),
> + regname, (offset + size - 1), offset, regname, (regsize - 1));
I don’t think "%ld" is portable for size_t. Should that be "%zu"?
> +
> + register_status reg_status
> + = regcache->cooked_read_part (regnum, offset, size, 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 for the register listing in the target
> + description to be in the same order in each regeset as the
> + intended DWARF numbering order. Currently this is 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: [BEGIN, END), where BEGIN 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_data *data = get_intelgt_gdbarch_data (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)
> +{
> + dprintf ("start_pc: %lx", start_pc);
CORE_ADDR is not a long on all configurations, I think using "%s" and
paddress() would be preffered here.
> + 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);
> +
> + dprintf ("post prologue pc: %lx", post_prologue_pc);
Same regarding %lx
> +
> + 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 (gdbarch *gdbarch, value *function,
> + type *valtype, regcache *regcache,
> + gdb_byte *readbuf, const gdb_byte *writebuf)
> +{
> + gdb_assert_not_reached ("intelgt_return_value is to be implemented later.");
> +}
I think nowadays, the return_value_as_value callback is preferred (I am
not sure if at least one needs to be provided, or if it is fine to not
implement any).
> +
> +/* 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_data *data = get_intelgt_gdbarch_data (gdbarch);
> +
> + int actual_regnum = data->framedesc_base_regnum ();
> +
> + /* Unwind the actual GRF register. */
> + return frame_unwind_register_value (this_frame, actual_regnum);
> +}
> +
> +static void
> +intelgt_init_reg (gdbarch *gdbarch, int regnum, dwarf2_frame_state_reg *reg,
> + const frame_info_ptr &this_frame)
Missing comment?
> +{
> + 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_data *data = get_intelgt_gdbarch_data (gdbarch);
> + gdb_assert (data->isabase_regnum != -1);
> +
> + uint64_t isabase = 0;
should isabase be a CORE_ADDRESS?
> + 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);
> + dprintf ("prev_ip: %lx", prev_ip);
Prefer paddress.
> +
> + /* 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: Other tdeps populate and use the cache. */
> +
> + /* Try to use symbol information to get the current start address. */
> + CORE_ADDR func;
> +
> + if (get_frame_func_if_available (this_frame, &func))
> + {
> + /* Use the current PC as a fallback if no symbol info is available. */
> + if (func == 0)
> + func = get_frame_pc (this_frame);
> +
> + /* FIXME: Because there is no full notion of stack, it
> + should be OK to ignore the SP reg. Currently, we cannot use SP
> + even if we want to, because SP's size is 16 bytes whereas
> + CORE_ADDR is 8. */
> + *this_id = frame_id_build_unavailable_stack (func);
> + }
> + else
> + *this_id = outer_frame_id;
> +}
> +
> +static value *
> +intelgt_frame_prev_register (const frame_info_ptr &this_frame,
> + void **this_prologue_cache, int regnum)
Missing comment?
> +{
> + dprintf ("regnum %d", regnum);
> +
> + gdbarch *arch = get_frame_arch (this_frame);
> + /* FIXME: Do the values below exist in an ABI? */
> + constexpr int STORAGE_REG_RET_PC = 1;
> + intelgt_gdbarch_data *data = get_intelgt_gdbarch_data (arch);
> + int STORAGE_REG_SP = data->framedesc_base_regnum ();
> +
> + if (regnum == intelgt_pseudo_register_num (arch, "ip"))
> + return frame_unwind_got_register (this_frame, regnum,
> + STORAGE_REG_RET_PC);
> + else if (regnum == gdbarch_sp_regnum (arch))
> + return frame_unwind_got_register (this_frame, regnum,
> + STORAGE_REG_SP);
> + else
> + return frame_unwind_got_register (this_frame, regnum, regnum);
> +}
> +
> +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 */
> + intelgt_frame_prev_register, /* 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, struct bp_target_info *bp)
> +{
> + dprintf ("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. */
> + dprintf ("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)
> + dprintf ("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)
> +{
> + dprintf ("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)
> + dprintf ("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)
> +{
> + dprintf ("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. */
> + dprintf ("Failed to read memory at %s (%s).",
> + paddress (gdbarch, pc), strerror (err));
> + return err;
> + }
> +
> + const bool is_bkpt = intelgt::has_breakpoint (inst);
> +
> + dprintf ("%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)
> +{
> + dprintf ("*pcptr: %lx", *pcptr);
Use paddress.
> +
> + 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)
> +{
> + dprintf ("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;
> +}
> +
> +/* Utility function to lookup 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_data *data = get_intelgt_gdbarch_data (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 ());
> +}
> +
> +static CORE_ADDR
> +intelgt_read_pc (readable_regcache *regcache)
Missing comment.
> +{
> + gdbarch *arch = regcache->arch ();
> + intelgt_gdbarch_data *data = get_intelgt_gdbarch_data (arch);
> +
> + /* Instruction pointer is stored in CR0.2. */
> + uint32_t ip;
> + intelgt_read_register_part (regcache, data->cr0_regnum,
> + sizeof (uint32_t) * 2, sizeof (uint32_t),
> + (gdb_byte *) &ip, _("Cannot compute PC."));
> +
> + /* Program counter is $ip + $isabase. */
> + CORE_ADDR isabase = intelgt_get_isabase (regcache);
> + return isabase + ip;
> +}
> +
> +static void
> +intelgt_write_pc (struct regcache *regcache, CORE_ADDR pc)
Missing comment.
> +{
> + 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)
I guess the "c++" way of writing UINT32_MAX is
"std::numeric_limits<uint32_t>::max ()", but I don’t really mind the
C-style macro either.
> + error ("Can't update $pc to value 0x%lx, out of range", pc);
> +
> + intelgt_gdbarch_data *data = get_intelgt_gdbarch_data (arch);
> +
> + /* Instruction pointer is stored in CR0.2. */
> + uint32_t ip = pc - isabase;
> + regcache->cooked_write_part (data->cr0_regnum, sizeof (uint32_t) * 2,
> + sizeof (uint32_t), (gdb_byte *) &ip);
> +}
> +
> +/* Return the name of pseudo-register REGNUM. */
> +
> +static const char *
> +intelgt_pseudo_register_name (gdbarch *arch, int regnum)
> +{
> + intelgt_gdbarch_data *data = get_intelgt_gdbarch_data (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_data *data = get_intelgt_gdbarch_data (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_data *data = get_intelgt_gdbarch_data (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_data *data = get_intelgt_gdbarch_data (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_data *data = get_intelgt_gdbarch_data (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:
> +
Is that empty line intentional?
> + 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, nullptr);
> + intelgt_gdbarch_data *data = get_intelgt_gdbarch_data (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 (gdbarch, intelgt_return_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);
> +
> + 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."),
I think you missed a space between "debugging" and "is".
> + nullptr,
> + show_intelgt_debug,
> + &setdebuglist, &showdebuglist);
> +}
> diff --git a/gdb/regcache.c b/gdb/regcache.c
> index f04354d822f..dc9266a7adc 100644
> --- a/gdb/regcache.c
> +++ b/gdb/regcache.c
> @@ -1932,9 +1932,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
Best,
Lancelot.
^ permalink raw reply [flat|nested] 77+ messages in thread
* RE: [PATCH 06/46] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture
2024-08-26 16:27 ` Lancelot SIX
@ 2024-08-29 13:09 ` Aktemur, Tankut Baris
2024-08-29 19:11 ` Lancelot SIX
0 siblings, 1 reply; 77+ messages in thread
From: Aktemur, Tankut Baris @ 2024-08-29 13:09 UTC (permalink / raw)
To: Lancelot SIX, gdb-patches; +Cc: Metzger, Markus T
On Monday, August 26, 2024 6:28 PM, Lancelot SIX wrote:
> Hi Baris,
>
> I have included some comments below. Since i am not familiar with the
> intelgt architectures, I won’t comment much on the architecture specific
> parts.
Hi Lancelot,
Thanks for the feedback!
> On 02/07/2024 12:42, Tankut Baris Aktemur wrote:
> > 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, IA, 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 | 2 +
> > gdb/configure.tgt | 5 +
> > gdb/intelgt-tdep.c | 987 +++++++++++++++++++++++++++++++++++++++++++++
> > gdb/regcache.c | 6 +-
> > 4 files changed, 999 insertions(+), 1 deletion(-)
> > create mode 100755 gdb/intelgt-tdep.c
> >
> > diff --git a/gdb/Makefile.in b/gdb/Makefile.in
> > index 1c697bf0ab1..830311100b0 100644
> > --- a/gdb/Makefile.in
> > +++ b/gdb/Makefile.in
> > @@ -750,11 +750,13 @@ 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 \
> > 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 8326c458eb1..5764db9f199 100644
> > --- a/gdb/configure.tgt
> > +++ b/gdb/configure.tgt
> > @@ -356,6 +356,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"
>
> The "arch/intelgt.c" file is not included in this patch. Should that
> part of the configure.tgt change be in another patch?
The "arch/intelgt.c" file was introduced in the previous patch (05/46) to
keep the patches somewhat smaller. The configure.tgt change most essentially
needs the tdep file, so it had made more sense to have the change in this
patch.
> > + ;;
> > +
> > iq2000-*-*)
> > gdb_target_obs="iq2000-tdep.o"
> > ;;
> > diff --git a/gdb/intelgt-tdep.c b/gdb/intelgt-tdep.c
> > new file mode 100755
> > index 00000000000..83cddec8d4b
> > --- /dev/null
> > +++ b/gdb/intelgt-tdep.c
> > @@ -0,0 +1,987 @@
> > +/* 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 "defs.h"
>
> I don’t think this is required anymore. See:
>
> commit 18d2988e5da8919514c76b83e2c0b56e439018bd
>
> gdb, gdbserver, gdbsupport: remove includes of early headers
>
Thanks. I'm also removing
#include "gdbsupport/common-defs.h"
from arch/intelgt.c, and
#include "server.h"
from two gdbserver files we introduce later in the series.
> > +#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;
> > +
> > +#define dprintf(...) \
> > + do \
> > + { \
> > + if (intelgt_debug) \
> > + { \
> > + gdb_printf (gdb_stdlog, "%s: ", __func__); \
> > + gdb_printf (gdb_stdlog, __VA_ARGS__); \
> > + gdb_printf (gdb_stdlog, "\n"); \
> > + } \
> > + } \
> > + while (0)
>
> Could this have been done using "debug_prefixed_printf_cond" (from
> gdbsupport/common-debug.h)? This would allow consistent debug outputs
> across GDB.
Yes, makes sense. We will include this change in the next revision, where
the function is called intelgt_debug_printf.
> > +
> > +/* Regnum pair describing the assigned regnum range for a single
> > + regset. */
> > +
> > +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);
>
> I have not checked yet other patches, is this function going to be used
> later, if do does the later patch using it will remove the maybe_unused?
The function is used in a subsequent patch, but the use is still guarded by an #ifdef
that depends on the availability of a library for disassembling. Hence, we are not
removing the maybe_unused in this patch series yet. But it would be removed in the
future with prospective downstream patches.
> > +
> > +/* The 'gdbarch_data' stuff specific for this architecture. */
> > +
> > +struct intelgt_gdbarch_data
> > +{
> > + /* $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-register 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_data ()
> > + {
> > + memset (®set_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;
> > + }
> > +};
> > +
> > +static const registry<gdbarch>::key<intelgt_gdbarch_data>
> > + intelgt_gdbarch_data_handle;
> > +
> > +static intelgt_gdbarch_data *
> > +get_intelgt_gdbarch_data (gdbarch *gdbarch)
> > +{
> > + intelgt_gdbarch_data *result = intelgt_gdbarch_data_handle.get (gdbarch);
> > + if (result == nullptr)
> > + result = intelgt_gdbarch_data_handle.emplace (gdbarch);
> > + return result;
> > +}
> > +
> > +/* 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, size_t size, 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 + size > regsize)
> > + error (_("%s[%ld:%ld] is outside the range of %s[%d:0]."),
> > + regname, (offset + size - 1), offset, regname, (regsize - 1));
>
> I don’t think "%ld" is portable for size_t. Should that be "%zu"?
"%zu" seems right here. Updated the local branch.
> > +
> > + register_status reg_status
> > + = regcache->cooked_read_part (regnum, offset, size, 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 for the register listing in the target
> > + description to be in the same order in each regeset as the
> > + intended DWARF numbering order. Currently this is 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: [BEGIN, END), where BEGIN 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_data *data = get_intelgt_gdbarch_data (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)
> > +{
> > + dprintf ("start_pc: %lx", start_pc);
>
> CORE_ADDR is not a long on all configurations, I think using "%s" and
> paddress() would be preffered here.
Starting with
commit 213291361b4ddb2d05b8c89bf47d23ca4306912e
Author: Tom Tromey <tom@tromey.com>
Date: Thu Mar 12 13:32:15 2020 -0600
Change gdbsupport not to rely on BFD
CORE_ADDR is defined as
typedef uint64_t CORE_ADDR;
But I agree that paddress would be better, at least to prepend "0x" and be consistent
with the other parts of GDB.
> > + 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);
> > +
> > + dprintf ("post prologue pc: %lx", post_prologue_pc);
>
> Same regarding %lx
Done.
> > +
> > + 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 (gdbarch *gdbarch, value *function,
> > + type *valtype, regcache *regcache,
> > + gdb_byte *readbuf, const gdb_byte *writebuf)
> > +{
> > + gdb_assert_not_reached ("intelgt_return_value is to be implemented later.");
> > +}
>
> I think nowadays, the return_value_as_value callback is preferred (I am
> not sure if at least one needs to be provided, or if it is fine to not
> implement any).
For "return_value", the comments in gdbarch_components.py say
NOTE: it is better to implement return_value_as_value instead, as that
method can properly handle variably-sized types.
The comments for "return_value_as_value" say
predefault="default_gdbarch_return_value",
# If we're using the default, then the other method must be set;
# but if we aren't using the default here then the other method
# must not be set.
So, you're right. At the moment, the implementation above serves only the purpose
of satisfying gdbarch expectations. We plan to upstream infcall support and return
value handling after the basic target support goes in. Meanwhile, however, we can
replace the return_value implementation above with "return_value_as_value" instead.
It's a placeholder anyway.
> > +
> > +/* 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_data *data = get_intelgt_gdbarch_data (gdbarch);
> > +
> > + int actual_regnum = data->framedesc_base_regnum ();
> > +
> > + /* Unwind the actual GRF register. */
> > + return frame_unwind_register_value (this_frame, actual_regnum);
> > +}
> > +
> > +static void
> > +intelgt_init_reg (gdbarch *gdbarch, int regnum, dwarf2_frame_state_reg *reg,
> > + const frame_info_ptr &this_frame)
>
> Missing comment?
Adding
/* Architecture-specific register state initialization. */
> > +{
> > + 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_data *data = get_intelgt_gdbarch_data (gdbarch);
> > + gdb_assert (data->isabase_regnum != -1);
> > +
> > + uint64_t isabase = 0;
>
> should isabase be a CORE_ADDRESS?
In some cases, an address obtained from a register (e.g. application IP in the
CR0.2 register) is 32 bits. In this case, the isabase register is 64 bits. We
wanted to make this explicit in the code.
> > + 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);
> > + dprintf ("prev_ip: %lx", prev_ip);
>
> Prefer paddress.
Done.
> > +
> > + /* 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: Other tdeps populate and use the cache. */
> > +
> > + /* Try to use symbol information to get the current start address. */
> > + CORE_ADDR func;
> > +
> > + if (get_frame_func_if_available (this_frame, &func))
> > + {
> > + /* Use the current PC as a fallback if no symbol info is available. */
> > + if (func == 0)
> > + func = get_frame_pc (this_frame);
> > +
> > + /* FIXME: Because there is no full notion of stack, it
> > + should be OK to ignore the SP reg. Currently, we cannot use SP
> > + even if we want to, because SP's size is 16 bytes whereas
> > + CORE_ADDR is 8. */
> > + *this_id = frame_id_build_unavailable_stack (func);
> > + }
> > + else
> > + *this_id = outer_frame_id;
> > +}
> > +
> > +static value *
> > +intelgt_frame_prev_register (const frame_info_ptr &this_frame,
> > + void **this_prologue_cache, int regnum)
>
> Missing comment?
Adding
/* The "prev_register" callback function for the unwinder. */
> > +{
> > + dprintf ("regnum %d", regnum);
> > +
> > + gdbarch *arch = get_frame_arch (this_frame);
> > + /* FIXME: Do the values below exist in an ABI? */
> > + constexpr int STORAGE_REG_RET_PC = 1;
> > + intelgt_gdbarch_data *data = get_intelgt_gdbarch_data (arch);
> > + int STORAGE_REG_SP = data->framedesc_base_regnum ();
> > +
> > + if (regnum == intelgt_pseudo_register_num (arch, "ip"))
> > + return frame_unwind_got_register (this_frame, regnum,
> > + STORAGE_REG_RET_PC);
> > + else if (regnum == gdbarch_sp_regnum (arch))
> > + return frame_unwind_got_register (this_frame, regnum,
> > + STORAGE_REG_SP);
> > + else
> > + return frame_unwind_got_register (this_frame, regnum, regnum);
> > +}
> > +
> > +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 */
> > + intelgt_frame_prev_register, /* 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, struct bp_target_info *bp)
> > +{
> > + dprintf ("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. */
> > + dprintf ("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)
> > + dprintf ("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)
> > +{
> > + dprintf ("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)
> > + dprintf ("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)
> > +{
> > + dprintf ("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. */
> > + dprintf ("Failed to read memory at %s (%s).",
> > + paddress (gdbarch, pc), strerror (err));
> > + return err;
> > + }
> > +
> > + const bool is_bkpt = intelgt::has_breakpoint (inst);
> > +
> > + dprintf ("%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)
> > +{
> > + dprintf ("*pcptr: %lx", *pcptr);
>
> Use paddress.
Done.
> > +
> > + 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)
> > +{
> > + dprintf ("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;
> > +}
> > +
> > +/* Utility function to lookup 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_data *data = get_intelgt_gdbarch_data (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 ());
> > +}
> > +
> > +static CORE_ADDR
> > +intelgt_read_pc (readable_regcache *regcache)
>
> Missing comment.
Adding
/* The "read_pc" gdbarch method. */
> > +{
> > + gdbarch *arch = regcache->arch ();
> > + intelgt_gdbarch_data *data = get_intelgt_gdbarch_data (arch);
> > +
> > + /* Instruction pointer is stored in CR0.2. */
> > + uint32_t ip;
> > + intelgt_read_register_part (regcache, data->cr0_regnum,
> > + sizeof (uint32_t) * 2, sizeof (uint32_t),
> > + (gdb_byte *) &ip, _("Cannot compute PC."));
> > +
> > + /* Program counter is $ip + $isabase. */
> > + CORE_ADDR isabase = intelgt_get_isabase (regcache);
> > + return isabase + ip;
> > +}
> > +
> > +static void
> > +intelgt_write_pc (struct regcache *regcache, CORE_ADDR pc)
>
> Missing comment.
Adding
/* The "write_pc" gdbarch method. */
> > +{
> > + 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)
>
> I guess the "c++" way of writing UINT32_MAX is
> "std::numeric_limits<uint32_t>::max ()", but I don’t really mind the
> C-style macro either.
Ack.
> > + error ("Can't update $pc to value 0x%lx, out of range", pc);
> > +
> > + intelgt_gdbarch_data *data = get_intelgt_gdbarch_data (arch);
> > +
> > + /* Instruction pointer is stored in CR0.2. */
> > + uint32_t ip = pc - isabase;
> > + regcache->cooked_write_part (data->cr0_regnum, sizeof (uint32_t) * 2,
> > + sizeof (uint32_t), (gdb_byte *) &ip);
> > +}
> > +
> > +/* Return the name of pseudo-register REGNUM. */
> > +
> > +static const char *
> > +intelgt_pseudo_register_name (gdbarch *arch, int regnum)
> > +{
> > + intelgt_gdbarch_data *data = get_intelgt_gdbarch_data (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_data *data = get_intelgt_gdbarch_data (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_data *data = get_intelgt_gdbarch_data (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_data *data = get_intelgt_gdbarch_data (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_data *data = get_intelgt_gdbarch_data (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:
> > +
>
> Is that empty line intentional?
Yes. We wanted to separate two groups of device ids. But I see that
in the XE_HPG cases above, we didn't do such separation. For consistency,
we'll either remove the blank line here or add blank lines above.
> > + 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, nullptr);
> > + intelgt_gdbarch_data *data = get_intelgt_gdbarch_data (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 (gdbarch, intelgt_return_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);
> > +
> > + 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."),
>
> I think you missed a space between "debugging" and "is".
Fixed.
> > + nullptr,
> > + show_intelgt_debug,
> > + &setdebuglist, &showdebuglist);
> > +}
> > diff --git a/gdb/regcache.c b/gdb/regcache.c
> > index f04354d822f..dc9266a7adc 100644
> > --- a/gdb/regcache.c
> > +++ b/gdb/regcache.c
> > @@ -1932,9 +1932,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
>
> Best,
> Lancelot.
Thanks again for the feedback.
-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] 77+ messages in thread
* Re: [PATCH 06/46] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture
2024-08-29 13:09 ` Aktemur, Tankut Baris
@ 2024-08-29 19:11 ` Lancelot SIX
2024-09-06 9:15 ` Aktemur, Tankut Baris
0 siblings, 1 reply; 77+ messages in thread
From: Lancelot SIX @ 2024-08-29 19:11 UTC (permalink / raw)
To: Aktemur, Tankut Baris, gdb-patches; +Cc: Metzger, Markus T
>>> +/* Return the PC of the first real instruction. */
>>> +
>>> +static CORE_ADDR
>>> +intelgt_skip_prologue (gdbarch *gdbarch, CORE_ADDR start_pc)
>>> +{
>>> + dprintf ("start_pc: %lx", start_pc);
>>
>> CORE_ADDR is not a long on all configurations, I think using "%s" and
>> paddress() would be preffered here.
>
> Starting with
>
> commit 213291361b4ddb2d05b8c89bf47d23ca4306912e
> Author: Tom Tromey <tom@tromey.com>
> Date: Thu Mar 12 13:32:15 2020 -0600
>
> Change gdbsupport not to rely on BFD
>
> CORE_ADDR is defined as
>
> typedef uint64_t CORE_ADDR;
>
> But I agree that paddress would be better, at least to prepend "0x" and be consistent
> with the other parts of GDB.
>
Sorry, I think I took a shortcut in my previous mail. On Linux,
uint64_t is indeed implemented using a "unsigned long", but on windows
for example uint64_t is implemented using "unsiged long long". This
means that "%lx" would work on Linux, but not on Windows where "%llx" is
needed. This is why I say that "%lx" is not portable.
The "standard" C way to solve this is to use `"%" PRIx64`, but those
macros are not used in GDB because they make the formats clunky.
Hope that clarified my initial comment.
Best,
Lancelot.
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH 09/46] gdbsupport, filestuff, ze: temporary files
2024-07-02 11:42 ` [PATCH 09/46] gdbsupport, filestuff, ze: temporary files Tankut Baris Aktemur
@ 2024-08-31 0:11 ` Lancelot SIX
2024-09-06 14:52 ` Metzger, Markus T
0 siblings, 1 reply; 77+ messages in thread
From: Lancelot SIX @ 2024-08-31 0:11 UTC (permalink / raw)
To: Tankut Baris Aktemur, gdb-patches
Hi Baris,
I have a couple of comments inlined below.
On 02/07/2024 12:42, Tankut Baris Aktemur wrote:
> 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 | 39 +++++++++++++++++++++++++++++++++++++++
> gdbsupport/filestuff.h | 6 ++++++
> 2 files changed, 45 insertions(+)
>
> diff --git a/gdbsupport/filestuff.cc b/gdbsupport/filestuff.cc
> index 5c427e11b02..0ffc8531095 100644
> --- a/gdbsupport/filestuff.cc
> +++ b/gdbsupport/filestuff.cc
> @@ -17,12 +17,15 @@
> along with this program. If not, see <http://www.gnu.org/licenses/>. */
>
> #include "filestuff.h"
> +#include "pathstuff.h"
> #include "gdb_vecs.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>
> @@ -545,3 +548,39 @@ 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 ()
> + {
> + for (const std::string& file : files)
I think the space should be before the "&" here.
> + unlink (file.c_str ());
I am not entirely sure, but should the value returned by unlink be
checked and some error/warning message printed if non-0?
> + }
Might be worth making this non copyable/assignable (we don’t want to
accidentally make a copy and double delete files)? Ideally that’s a
singelton.
> +};
> +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"), name.data ());
Could be perror_with_name so it uses errno to build a better error message.
> +
> + gdb_file_up file { fd.to_file ("wb") };
> + if (file.get () == nullptr)
> + error (_("failed to open %s"), name.data ());
> +
> + base = name.data ();
> + tmpfilekeeper.files.emplace_back (base);
> +
> + return file;
> +}
> diff --git a/gdbsupport/filestuff.h b/gdbsupport/filestuff.h
> index e2ee141d46f..6b7d1ecf935 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. */
>
Best,
Lancelot.
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH 14/46] gdb, infrun, ze: allow saving process events
2024-07-02 11:42 ` [PATCH 14/46] gdb, infrun, ze: allow saving process events Tankut Baris Aktemur
@ 2024-08-31 18:00 ` Lancelot SIX
2024-09-09 14:33 ` Metzger, Markus T
0 siblings, 1 reply; 77+ messages in thread
From: Lancelot SIX @ 2024-08-31 18:00 UTC (permalink / raw)
To: Tankut Baris Aktemur, gdb-patches
Hi Baris,
On 02/07/2024 12:42, Tankut Baris Aktemur wrote:
> 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 | 6 ++++++
> gdb/infrun.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 58 insertions(+)
>
> diff --git a/gdb/inferior.h b/gdb/inferior.h
> index c08261bdcd3..301594a1558 100644
> --- a/gdb/inferior.h
> +++ b/gdb/inferior.h
> @@ -319,6 +319,12 @@ struct inferior_control_state
>
> /* See the definition of stop_kind above. */
> enum stop_kind stop_soon;
> +
> + /* The waitstatus for this inferior's last event. */
> + struct target_waitstatus waitstatus {};
> +
> + /* If true WAITSTATUS hasn't been handled yet. */
> + bool waitstatus_pending_p = false;
Could that be a std::optional<target_waitstatus>? This way there is
only one member that encapsulates both the bool (has_value ()) and the
value itself if present, and there is no risk of two members getting out
of sync.
Best,
Lancelot.
> };
>
> /* Initialize the inferior-related global state. */
> diff --git a/gdb/infrun.c b/gdb/infrun.c
> index 1f32a63ad54..1133ef3492f 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_pending_p)
> + {
> + 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_pending_p)
> + {
> + /* 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_pending_p = false;
> +
> + 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,23 @@ 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;
> + inf->control.waitstatus_pending_p = true;
> + }
> else
> {
> thread_info *t = event.target->find_thread (event.ptid);
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH 15/46] gdb, ze: add TARGET_WAITKIND_UNAVAILABLE
2024-07-02 11:42 ` [PATCH 15/46] gdb, ze: add TARGET_WAITKIND_UNAVAILABLE Tankut Baris Aktemur
@ 2024-09-04 10:47 ` Lancelot SIX
2024-09-10 7:47 ` Metzger, Markus T
0 siblings, 1 reply; 77+ messages in thread
From: Lancelot SIX @ 2024-09-04 10:47 UTC (permalink / raw)
To: Tankut Baris Aktemur, gdb-patches
On 02/07/2024 12:42, Tankut Baris Aktemur wrote:
> 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 539b11695d9..80372d023da 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 73f6895fe46..91496c594e4 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.
Hi Baris,
I am not entirely sure I fully understand all the implications of this
model.
I can see a scenario where:
- you are in target-async & all-stop, want to stop_all_threads for a
given process (A),
- some of the device’s hardware threads are still busy executing work
for a process B, so those hardware threads are reported as "unavailable".
After that, all (current) threads of process A are stopped, but as soon
as HW is done with working on the dispatch of process B, it can start
executing more work from the current dispatch of process A. In this
configuration, can’t we end up in a situation where we think that all
activity is suspended, but still have "things actively running"?
I have not read the entire series yet, so maybe the solution to that
comes later, but I was wondering if there was more semantics attached to
"unavailable" to handle this scenario, or if another approach will come
later.
Writing this I also realize that the approach we use for AMDGPU to
prevent creation of new waves has not been sent upstream yet. Probably
because the test to actually validate this requires debug info, but we
should probably submit it as this is quite fundamental to properly
controlling a GPU. In short, the way it is done for AMDGPU is we have a
"prevent_new_threads" target method that the core can use when it needs
to be sure that no new threads can be created[1] (stop_all_threads is
one such place).
Best,
Lancelot.
[1]
https://github.com/ROCm/ROCgdb/commit/0260ac17a5e664c504092724f6bb393e18d0a6ba
>
> If PTID is minus_one_ptid, go over all threads of TARG.
>
> diff --git a/gdb/infrun.c b/gdb/infrun.c
> index 1133ef3492f..cc80a4f7f96 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);
> @@ -6625,6 +6637,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 41765b102bc..d70a3d616ca 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 7746abd1be2..25584db5b5a 100644
> --- a/gdb/remote.c
> +++ b/gdb/remote.c
> @@ -4962,7 +4962,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 9e9b5633b12..1cd0eee2236 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 7d5ad3f9776..3d1d1b918c7 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 4ee46936861..71dfb0d0fb1 100644
> --- a/gdb/thread.c
> +++ b/gdb/thread.c
> @@ -970,7 +970,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)
^ permalink raw reply [flat|nested] 77+ messages in thread
* RE: [PATCH 06/46] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture
2024-08-29 19:11 ` Lancelot SIX
@ 2024-09-06 9:15 ` Aktemur, Tankut Baris
2024-09-30 15:35 ` Alexandra Petlanova Hajkova
0 siblings, 1 reply; 77+ messages in thread
From: Aktemur, Tankut Baris @ 2024-09-06 9:15 UTC (permalink / raw)
To: Lancelot SIX, gdb-patches; +Cc: Metzger, Markus T
On Thursday, August 29, 2024 9:11 PM, Lancelot SIX wrote:
> >>> +/* Return the PC of the first real instruction. */
> >>> +
> >>> +static CORE_ADDR
> >>> +intelgt_skip_prologue (gdbarch *gdbarch, CORE_ADDR start_pc)
> >>> +{
> >>> + dprintf ("start_pc: %lx", start_pc);
> >>
> >> CORE_ADDR is not a long on all configurations, I think using "%s" and
> >> paddress() would be preffered here.
> >
> > Starting with
> >
> > commit 213291361b4ddb2d05b8c89bf47d23ca4306912e
> > Author: Tom Tromey <tom@tromey.com>
> > Date: Thu Mar 12 13:32:15 2020 -0600
> >
> > Change gdbsupport not to rely on BFD
> >
> > CORE_ADDR is defined as
> >
> > typedef uint64_t CORE_ADDR;
> >
> > But I agree that paddress would be better, at least to prepend "0x" and be consistent
> > with the other parts of GDB.
> >
>
> Sorry, I think I took a shortcut in my previous mail. On Linux,
> uint64_t is indeed implemented using a "unsigned long", but on windows
> for example uint64_t is implemented using "unsiged long long". This
> means that "%lx" would work on Linux, but not on Windows where "%llx" is
> needed. This is why I say that "%lx" is not portable.
>
> The "standard" C way to solve this is to use `"%" PRIx64`, but those
> macros are not used in GDB because they make the formats clunky.
>
> Hope that clarified my initial comment.
Yes, thanks for the clarification.
-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] 77+ messages in thread
* RE: [PATCH 09/46] gdbsupport, filestuff, ze: temporary files
2024-08-31 0:11 ` Lancelot SIX
@ 2024-09-06 14:52 ` Metzger, Markus T
0 siblings, 0 replies; 77+ messages in thread
From: Metzger, Markus T @ 2024-09-06 14:52 UTC (permalink / raw)
To: Lancelot SIX; +Cc: Aktemur, Tankut Baris, gdb-patches
Hello Lancelot,
Thanks for your review.
>> + ~tmpfilekeeper_s ()
>> + {
>> + for (const std::string& file : files)
>> + unlink (file.c_str ());
>
>I am not entirely sure, but should the value returned by unlink be
>checked and some error/warning message printed if non-0?
I don't think we want to throw inside a dtor but a warning makes sense.
>> + scoped_fd fd { gdb_mkostemp_cloexec (name.data (), O_BINARY) };
>> + if (fd.get () == -1)
>> + error (_("failed to create temporary file %s"), name.data ());
>
>Could be perror_with_name so it uses errno to build a better error message.
This won't allow me to add the filename unless I create the error string
in a string buffer. I would rather add safe_strerror (errno) to the error
string.
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] 77+ messages in thread
* RE: [PATCH 14/46] gdb, infrun, ze: allow saving process events
2024-08-31 18:00 ` Lancelot SIX
@ 2024-09-09 14:33 ` Metzger, Markus T
0 siblings, 0 replies; 77+ messages in thread
From: Metzger, Markus T @ 2024-09-09 14:33 UTC (permalink / raw)
To: Lancelot SIX; +Cc: Aktemur, Tankut Baris, gdb-patches
Hello Lancelot,
Thanks for your review.
>> + /* The waitstatus for this inferior's last event. */
>> + struct target_waitstatus waitstatus {};
>> +
>> + /* If true WAITSTATUS hasn't been handled yet. */
>> + bool waitstatus_pending_p = false;
>
>Could that be a std::optional<target_waitstatus>? This way there is
>only one member that encapsulates both the bool (has_value ()) and the
>value itself if present, and there is no risk of two members getting out
>of sync.
Sounds good.
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] 77+ messages in thread
* RE: [PATCH 15/46] gdb, ze: add TARGET_WAITKIND_UNAVAILABLE
2024-09-04 10:47 ` Lancelot SIX
@ 2024-09-10 7:47 ` Metzger, Markus T
0 siblings, 0 replies; 77+ messages in thread
From: Metzger, Markus T @ 2024-09-10 7:47 UTC (permalink / raw)
To: Lancelot SIX; +Cc: Aktemur, Tankut Baris, gdb-patches
Hello Lancelot,
Thanks for your review.
>I can see a scenario where:
>- you are in target-async & all-stop, want to stop_all_threads for a
>given process (A),
>- some of the device’s hardware threads are still busy executing work
>for a process B, so those hardware threads are reported as "unavailable".
>
>After that, all (current) threads of process A are stopped, but as soon
>as HW is done with working on the dispatch of process B, it can start
>executing more work from the current dispatch of process A. In this
>configuration, can’t we end up in a situation where we think that all
>activity is suspended, but still have "things actively running"?
We can indeed. This is unfortunate, but that's how the h/w works.
We're mitigating that by allowing only one active context on the device if
that context is debuggable. On current h/w, the 'working on the dispatch
of process B' scenario is not actually possible, but I still wanted to point
it out as a theoretical possibility.
We may still have threads appearing after we interrupted the inferior, e.g.
if the kernel is just starting up and we have not dispatched all threads yet.
We keep the 'break on dispatch' signal high for some time in the driver, so
this scenario should be rather unlikely, but we cannot rule it out completely,
so we still need to consider it.
A more likely scenario that leads to unavailable threads is if there is no
kernel submitted to the device or if the kernel is not saturating the device.
Unless we attach to a running program, for example, we get load events
before the first kernel is submitted, and all threads are unavailable while
we process those events.
If we resumed the host inferior and allowed it to submit kernels while
keeping device inferiors stopped, for example, we would actually see
device threads appearing and reporting breakpoint hits. In all-stop mode,
we would only learn about those events when we resumed device inferiors,
so we wouldn't notice any difference.
In non-stop mode, we would get events for threads that we thought were
unavailable. In practice, this doesn't make much difference, though. A thread
that reported an event will remain stopped until it is resumed. You'd really
only notice it if you tried to interrupt a thread that is currently unavailable.
That thread may report an event later. Also, another interrupt request may
succeed.
>Writing this I also realize that the approach we use for AMDGPU to
>prevent creation of new waves has not been sent upstream yet. Probably
>because the test to actually validate this requires debug info, but we
>should probably submit it as this is quite fundamental to properly
>controlling a GPU. In short, the way it is done for AMDGPU is we have a
>"prevent_new_threads" target method that the core can use when it needs
>to be sure that no new threads can be created[1] (stop_all_threads is
>one such place).
Unfortunately, we do not have such a mechanism.
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] 77+ messages in thread
* Re: [PATCH 06/46] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture
2024-09-06 9:15 ` Aktemur, Tankut Baris
@ 2024-09-30 15:35 ` Alexandra Petlanova Hajkova
2024-10-09 7:55 ` Aktemur, Tankut Baris
0 siblings, 1 reply; 77+ messages in thread
From: Alexandra Petlanova Hajkova @ 2024-09-30 15:35 UTC (permalink / raw)
To: Aktemur, Tankut Baris; +Cc: Lancelot SIX, gdb-patches, Metzger, Markus T
[-- Attachment #1: Type: text/plain, Size: 1171 bytes --]
Hi Baris,
this patch seems to crash GDB during the gdb.base/all-architectures-2.exp
test:
(gdb) IPASS: gdb.base/all-architectures-2.exp: tests: osabi=AIX:
arch=intelgt: endian=auto: print, float
disassemble 0x100,+4
Dump of assembler code from 0x100 to 0x104:
0x0000000000000100: ../../../binutils-gdb/gdb/arch-utils.c:1044:
internal-error: default_print_insn: Assertion `disassemble_fn != NULL'
failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
----- Backtrace -----
FAIL: gdb.base/all-architectures-2.exp: tests: osabi=AIX: arch=intelgt:
endian=auto: disassemble 0x100,+4 (GDB internal error)
Resyncing due to internal error.
0x4f47eb gdb_internal_backtrace_1
../../../binutils-gdb/gdb/bt-utils.c:121
0x4f47eb _Z22gdb_internal_backtracev
../../../binutils-gdb/gdb/bt-utils.c:167
0x9b20bf internal_vproblem
../../../binutils-gdb/gdb/utils.c:421
...
./../../binutils-gdb/gdb/arch-utils.c:1044: internal-error:
default_print_insn: Assertion `disassemble_fn != NULL' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Quit this debugging session? (y or n)
^ permalink raw reply [flat|nested] 77+ messages in thread
* RE: [PATCH 06/46] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture
2024-09-30 15:35 ` Alexandra Petlanova Hajkova
@ 2024-10-09 7:55 ` Aktemur, Tankut Baris
0 siblings, 0 replies; 77+ messages in thread
From: Aktemur, Tankut Baris @ 2024-10-09 7:55 UTC (permalink / raw)
To: Alexandra Petlanova Hajkova, gdb-patches; +Cc: Lancelot SIX, Metzger, Markus T
Hello Sasha,
On Monday, September 30, 2024 5:36 PM Alexandra Petlanova Hajkova wrote:
> Hi Baris,
>
> this patch seems to crash GDB during the gdb.base/all-architectures-2.exp test:
> ./../../binutils-gdb/gdb/arch-utils.c:1044: internal-error: default_print_insn: Assertion `disassemble_fn != NULL' failed.
Thanks for reporting this. The disassembly function is added two patches
later, in Patch 08/46 "gdb, intelgt: add disassemble feature for the Intel
GT architecture." Hence, I missed the problem in Patch 06/46.
I locally modified the patch with the fixup below. This fixes the failure.
Thanks!
-Baris
diff --git a/gdb/intelgt-tdep.c b/gdb/intelgt-tdep.c
index d4fb7f6ee2c..66647c9ed5a 100755
--- a/gdb/intelgt-tdep.c
+++ b/gdb/intelgt-tdep.c
@@ -550,6 +550,15 @@ intelgt_sw_breakpoint_from_kind (gdbarch *gdbarch, int kind, int *size)
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 lookup the pseudo-register number by name. Exact
amount of pseudo-registers may differ and thus fixed constants can't be
used for this. */
@@ -954,6 +963,9 @@ intelgt_gdbarch_init (gdbarch_info info, gdbarch_list *arches)
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;
}
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] 77+ messages in thread
* Re: [PATCH 02/46] bfd: add intelgt target to BFD
2024-07-02 11:42 ` [PATCH 02/46] bfd: add intelgt target to BFD Tankut Baris Aktemur
@ 2024-10-15 22:15 ` Thiago Jung Bauermann
2024-10-25 12:18 ` Aktemur, Tankut Baris
0 siblings, 1 reply; 77+ messages in thread
From: Thiago Jung Bauermann @ 2024-10-15 22:15 UTC (permalink / raw)
To: Tankut Baris Aktemur; +Cc: gdb-patches
Hello Baris,
I started reviewing this series, but I'm not sure how far into it I'll
be able to go... So I'm sending what I have so far.
Note that I'm not used to BFD code and its practices, so take the
following with a grain of salt.
Tankut Baris Aktemur <tankut.baris.aktemur@intel.com> writes:
> +/* 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;
Does it make sense to check whether the given abfd is of the expected
kind? E.g., something like:
const bfd_arch_info_type *arch_info = bfd_get_arch_info (abfd);
if (arch_info->arch != bfd_arch_intelgt \
|| arch_info->mach != bfd_mach_intelgt)
return NULL;
> +
> + 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;
Same question here, about checking whether abfd is correct.
> +
> + 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;
> +}
--
Thiago
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH 05/46] gdb, arch, intelgt: add intelgt arch definitions
2024-07-02 11:42 ` [PATCH 05/46] gdb, arch, intelgt: add intelgt arch definitions Tankut Baris Aktemur
2024-07-03 17:17 ` Guinevere Larsen
@ 2024-10-15 22:44 ` Thiago Jung Bauermann
2024-10-15 22:57 ` Thiago Jung Bauermann
1 sibling, 1 reply; 77+ messages in thread
From: Thiago Jung Bauermann @ 2024-10-15 22:44 UTC (permalink / raw)
To: Tankut Baris Aktemur; +Cc: gdb-patches
Tankut Baris Aktemur <tankut.baris.aktemur@intel.com> writes:
> diff --git a/gdb/arch/intelgt.h b/gdb/arch/intelgt.h
> new file mode 100644
> index 00000000000..a4154ddb8d3
> --- /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 (const gdb_byte inst[], int pos);
> +
> +/* Set the bit at POS in INST. */
> +
> +bool set_inst_bit (gdb_byte inst[], int pos);
> +
> +/* Clear the bit at POS in INST. */
> +
> +bool clear_inst_bit (gdb_byte inst[], int pos);
> +
> +static inline bool
> +is_compacted_inst (const gdb_byte inst[])
> +{
> + /* Check the CmptCtrl flag (bit 29). */
> + return inst[3] & 0x20;
> +}
> +
> +static inline int
> +breakpoint_bit_offset (const gdb_byte inst[])
> +{
> + return (is_compacted_inst (inst) ? 7 : 30);
> +}
> +
> +static inline bool
> +set_breakpoint (gdb_byte inst[])
> +{
> + return set_inst_bit (inst, breakpoint_bit_offset (inst));
> +}
> +
> +static inline bool
> +clear_breakpoint (gdb_byte inst[])
> +{
> + return clear_inst_bit (inst, breakpoint_bit_offset (inst));
> +}
> +
> +static inline bool
> +has_breakpoint (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 (const gdb_byte inst[])
> +{
> + return (is_compacted_inst (inst)
> + ? inst_length_compacted ()
> + : inst_length_full ());
> +}
> +
> +} /* namespace intelgt */
> +
> +#endif
All these functions defined in the "arch/intelgt.h" header which take a
gdb_byte inst[] as argument could be changed to take a
gdb::array_view<gdb_byte> or gdb::array_view<const gdb_byte> instead.
The advantage is that the size of the array is passed along with the
pointer, so the "if (pos < 0 || (MAX_INST_LENGTH * 8) <= pos)" checks
can be changed to "if (pos < 0 || inst.size () <= pos)" instead.
--
Thiago
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH 05/46] gdb, arch, intelgt: add intelgt arch definitions
2024-10-15 22:44 ` Thiago Jung Bauermann
@ 2024-10-15 22:57 ` Thiago Jung Bauermann
2024-10-25 12:18 ` Aktemur, Tankut Baris
0 siblings, 1 reply; 77+ messages in thread
From: Thiago Jung Bauermann @ 2024-10-15 22:57 UTC (permalink / raw)
To: Tankut Baris Aktemur; +Cc: gdb-patches
Sorry, forgot to add a few more comments I had here:
Thiago Jung Bauermann <thiago.bauermann@linaro.org> writes:
> Tankut Baris Aktemur <tankut.baris.aktemur@intel.com> writes:
>
>> +/* 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";
We use upper case for constants.
>> +
>> +/* 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
>> +};
We use upper case for enum constants.
>> +
>> +/* Map of dwarf_regset values to the target description
>> + feature names. */
>> +
>> +constexpr const char *dwarf_regset_features[regset_count] = {
Should "dwarf_regset_features" be upper case? Not sure, but I think so
since it's constexpr.
>> + 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,
>> +};
We use upper case for enum constants.
>> +
>> +/* Selected instruction control bit positions. */
>> +
>> +enum
>> +{
>> + /* The End Of Thread control. Only used for SEND and SENDC. */
>> + ctrl_eot = 34,
>> +};
We use upper case for enum constants.
>> +static inline unsigned int
>> +inst_length_compacted ()
>> +{
>> + return COMPACT_INST_LENGTH;
>> +}
>> +
>> +static inline unsigned int
>> +inst_length_full ()
>> +{
>> + return MAX_INST_LENGTH;
>> +}
Is there an advantage to defining and using these functions rather than
using COMPACT_INST_LENGTH and MAX_INST_LENGTH directly?
>> +static inline unsigned int
>> +inst_length (const gdb_byte inst[])
>> +{
>> + return (is_compacted_inst (inst)
>> + ? inst_length_compacted ()
>> + : inst_length_full ());
>> +}
>> +
>> +} /* namespace intelgt */
>> +
>> +#endif
>
> All these functions defined in the "arch/intelgt.h" header which take a
> gdb_byte inst[] as argument could be changed to take a
> gdb::array_view<gdb_byte> or gdb::array_view<const gdb_byte> instead.
>
> The advantage is that the size of the array is passed along with the
> pointer, so the "if (pos < 0 || (MAX_INST_LENGTH * 8) <= pos)" checks
> can be changed to "if (pos < 0 || inst.size () <= pos)" instead.
--
Thiago
^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH 06/46] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture
2024-07-02 11:42 ` [PATCH 06/46] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture Tankut Baris Aktemur
2024-08-26 16:27 ` Lancelot SIX
@ 2024-10-15 23:35 ` Thiago Jung Bauermann
2024-11-08 13:56 ` Aktemur, Tankut Baris
1 sibling, 1 reply; 77+ messages in thread
From: Thiago Jung Bauermann @ 2024-10-15 23:35 UTC (permalink / raw)
To: Tankut Baris Aktemur; +Cc: gdb-patches
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, IA,
What is "IA" here? I don't understand this part of the sentence.
> 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.
<snip>
> --- /dev/null
> +++ b/gdb/intelgt-tdep.c
> @@ -0,0 +1,987 @@
> +/* 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 "defs.h"
> +#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;
> +
> +#define dprintf(...) \
> + do \
> + { \
> + if (intelgt_debug) \
> + { \
> + gdb_printf (gdb_stdlog, "%s: ", __func__); \
> + gdb_printf (gdb_stdlog, __VA_ARGS__); \
> + gdb_printf (gdb_stdlog, "\n"); \
> + } \
> + } \
> + while (0)
> +
> +/* Regnum pair describing the assigned regnum range for a single
> + regset. */
Elsewhere in this patch you mention that END is exclusive. I think it
would be good to have that remark here as well.
> +
> +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);
> +
> +/* The 'gdbarch_data' stuff specific for this architecture. */
> +
> +struct intelgt_gdbarch_data
> +{
> + /* $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-register for the current target description. */
s/pseudo-register/pseudo-registers/
Also, suggest adding a space bbetween each element of this struct to
make it easier to read.
> + 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_data ()
> + {
> + memset (®set_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;
> + }
> +};
> +
> +static const registry<gdbarch>::key<intelgt_gdbarch_data>
> + intelgt_gdbarch_data_handle;
I believe the traditional way to store and fetch arch-specific
information associated with a gdbarch is using a subclass of
gdbarch_tdep_base, which is then obtained with
"gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch)".
See "struct aarch64_gdbarch_tdep" for an example.
Any reason why it was done this way instead?
> +
> +static intelgt_gdbarch_data *
The return value of this function can't be nullptr, so I think it's
better to return a reference rather than a pointer, which conveys that
information syntactically.
> +get_intelgt_gdbarch_data (gdbarch *gdbarch)
> +{
> + intelgt_gdbarch_data *result = intelgt_gdbarch_data_handle.get (gdbarch);
> + if (result == nullptr)
> + result = intelgt_gdbarch_data_handle.emplace (gdbarch);
> + return result;
> +}
> +
> +/* 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, size_t size, gdb_byte *buffer,
It's better to accept a gdb::array_view<gdb_byte> rather than a pointer
and a size.
> + 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 + size > regsize)
> + error (_("%s[%ld:%ld] is outside the range of %s[%d:0]."),
> + regname, (offset + size - 1), offset, regname, (regsize - 1));
> +
> + register_status reg_status
> + = regcache->cooked_read_part (regnum, offset, size, 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 for the register listing in the target
> + description to be in the same order in each regeset as the
> + intended DWARF numbering order. Currently this is always
Remove either "is" or "holds" from this sentence.
> + holds true when gdbserver generates the target description. */
> +
> +static int
> +intelgt_dwarf_reg_to_regnum (gdbarch *gdbarch, int num)
> +{
<snip>
> +/* Frame unwinding. */
> +
> +static void
> +intelgt_frame_this_id (const frame_info_ptr &this_frame,
> + void **this_prologue_cache, frame_id *this_id)
> +{
> + /* FIXME: Other tdeps populate and use the cache. */
> +
> + /* Try to use symbol information to get the current start address. */
> + CORE_ADDR func;
> +
> + if (get_frame_func_if_available (this_frame, &func))
> + {
> + /* Use the current PC as a fallback if no symbol info is available. */
> + if (func == 0)
> + func = get_frame_pc (this_frame);
> +
> + /* FIXME: Because there is no full notion of stack, it
> + should be OK to ignore the SP reg. Currently, we cannot use SP
> + even if we want to, because SP's size is 16 bytes whereas
> + CORE_ADDR is 8. */
Sorry, this made me curious: why is SP 16 bytes? Isn't 64-bit address
space enough for anyone?
> + *this_id = frame_id_build_unavailable_stack (func);
> + }
> + else
> + *this_id = outer_frame_id;
> +}
--
Thiago
^ permalink raw reply [flat|nested] 77+ messages in thread
* RE: [PATCH 02/46] bfd: add intelgt target to BFD
2024-10-15 22:15 ` Thiago Jung Bauermann
@ 2024-10-25 12:18 ` Aktemur, Tankut Baris
0 siblings, 0 replies; 77+ messages in thread
From: Aktemur, Tankut Baris @ 2024-10-25 12:18 UTC (permalink / raw)
To: Thiago Jung Bauermann, gdb-patches; +Cc: Metzger, Markus T
Hi Thiago,
On Wednesday, October 16, 2024 12:15 AM, Thiago Jung Bauermann wrote:
> Hello Baris,
>
> I started reviewing this series, but I'm not sure how far into it I'll
> be able to go... So I'm sending what I have so far.
>
> Note that I'm not used to BFD code and its practices, so take the
> following with a grain of salt.
Thanks a lot for checking the patches!
> Tankut Baris Aktemur <tankut.baris.aktemur@intel.com> writes:
>
> > +/* 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;
>
> Does it make sense to check whether the given abfd is of the expected
> kind? E.g., something like:
>
> const bfd_arch_info_type *arch_info = bfd_get_arch_info (abfd);
>
> if (arch_info->arch != bfd_arch_intelgt \
> || arch_info->mach != bfd_mach_intelgt)
> return NULL;
>
> > +
> > + 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;
>
> Same question here, about checking whether abfd is correct.
In the other targets' ...reloc_name_lookup and ...reloc_type_lookup
functions such a check was not performed. We tried to follow the existing
styles and approaches.
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] 77+ messages in thread
* RE: [PATCH 05/46] gdb, arch, intelgt: add intelgt arch definitions
2024-10-15 22:57 ` Thiago Jung Bauermann
@ 2024-10-25 12:18 ` Aktemur, Tankut Baris
0 siblings, 0 replies; 77+ messages in thread
From: Aktemur, Tankut Baris @ 2024-10-25 12:18 UTC (permalink / raw)
To: Thiago Jung Bauermann, gdb-patches; +Cc: Metzger, Markus T
Hi Thiago,
On Wednesday, October 16, 2024 12:57 AM, Thiago Jung Bauermann wrote:
> Sorry, forgot to add a few more comments I had here:
>
> Thiago Jung Bauermann <thiago.bauermann@linaro.org> writes:
>
> > Tankut Baris Aktemur <tankut.baris.aktemur@intel.com> writes:
> >
> >> +/* 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";
>
> We use upper case for constants.
I'll convert the constants and the enum values to uppercase.
> >> +
> >> +/* 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
> >> +};
>
> We use upper case for enum constants.
>
> >> +
> >> +/* Map of dwarf_regset values to the target description
> >> + feature names. */
> >> +
> >> +constexpr const char *dwarf_regset_features[regset_count] = {
>
> Should "dwarf_regset_features" be upper case? Not sure, but I think so
> since it's constexpr.
Yeah, I think it would be more consistent to do so.
> >> + 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,
> >> +};
>
> We use upper case for enum constants.
>
> >> +
> >> +/* Selected instruction control bit positions. */
> >> +
> >> +enum
> >> +{
> >> + /* The End Of Thread control. Only used for SEND and SENDC. */
> >> + ctrl_eot = 34,
> >> +};
>
> We use upper case for enum constants.
>
> >> +static inline unsigned int
> >> +inst_length_compacted ()
> >> +{
> >> + return COMPACT_INST_LENGTH;
> >> +}
> >> +
> >> +static inline unsigned int
> >> +inst_length_full ()
> >> +{
> >> + return MAX_INST_LENGTH;
> >> +}
>
> Is there an advantage to defining and using these functions rather than
> using COMPACT_INST_LENGTH and MAX_INST_LENGTH directly?
Not much. We'll remove the functions and use the constants directly.
> >> +static inline unsigned int
> >> +inst_length (const gdb_byte inst[])
> >> +{
> >> + return (is_compacted_inst (inst)
> >> + ? inst_length_compacted ()
> >> + : inst_length_full ());
> >> +}
> >> +
> >> +} /* namespace intelgt */
> >> +
> >> +#endif
> >
> > All these functions defined in the "arch/intelgt.h" header which take a
> > gdb_byte inst[] as argument could be changed to take a
> > gdb::array_view<gdb_byte> or gdb::array_view<const gdb_byte> instead.
> >
> > The advantage is that the size of the array is passed along with the
> > pointer, so the "if (pos < 0 || (MAX_INST_LENGTH * 8) <= pos)" checks
> > can be changed to "if (pos < 0 || inst.size () <= pos)" instead.
Thanks. This makes sense. We'll make those changes.
-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] 77+ messages in thread
* RE: [PATCH 06/46] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture
2024-10-15 23:35 ` Thiago Jung Bauermann
@ 2024-11-08 13:56 ` Aktemur, Tankut Baris
0 siblings, 0 replies; 77+ messages in thread
From: Aktemur, Tankut Baris @ 2024-11-08 13:56 UTC (permalink / raw)
To: Thiago Jung Bauermann, gdb-patches; +Cc: Metzger, Markus T
On Wednesday, October 16, 2024 1:35 AM, Thiago Jung Bauermann wrote:
> 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, IA,
>
> What is "IA" here? I don't understand this part of the sentence.
By IA, we meant "Intel Architecture". I changed it to "X86-64" to avoid
confusion.
> > 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.
>
> <snip>
>
> > --- /dev/null
> > +++ b/gdb/intelgt-tdep.c
> > @@ -0,0 +1,987 @@
> > +/* 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 "defs.h"
> > +#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;
> > +
> > +#define dprintf(...) \
> > + do \
> > + { \
> > + if (intelgt_debug) \
> > + { \
> > + gdb_printf (gdb_stdlog, "%s: ", __func__); \
> > + gdb_printf (gdb_stdlog, __VA_ARGS__); \
> > + gdb_printf (gdb_stdlog, "\n"); \
> > + } \
> > + } \
> > + while (0)
> > +
> > +/* Regnum pair describing the assigned regnum range for a single
> > + regset. */
>
> Elsewhere in this patch you mention that END is exclusive. I think it
> would be good to have that remark here as well.
Done.
> > +
> > +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);
> > +
> > +/* The 'gdbarch_data' stuff specific for this architecture. */
> > +
> > +struct intelgt_gdbarch_data
> > +{
> > + /* $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-register for the current target description. */
>
> s/pseudo-register/pseudo-registers/
>
> Also, suggest adding a space bbetween each element of this struct to
> make it easier to read.
Done.
> > + 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_data ()
> > + {
> > + memset (®set_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;
> > + }
> > +};
> > +
> > +static const registry<gdbarch>::key<intelgt_gdbarch_data>
> > + intelgt_gdbarch_data_handle;
>
> I believe the traditional way to store and fetch arch-specific
> information associated with a gdbarch is using a subclass of
> gdbarch_tdep_base, which is then obtained with
> "gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch)".
> See "struct aarch64_gdbarch_tdep" for an example.
>
> Any reason why it was done this way instead?
We were following the gdbarch data registry approach as done in linux-tdep.c
and a few other tdeps. The gdbarch_tdep<> approach, however, is more common
and we changed the code to use that instead. Thanks for pointing this out.
> > +
> > +static intelgt_gdbarch_data *
>
> The return value of this function can't be nullptr, so I think it's
> better to return a reference rather than a pointer, which conveys that
> information syntactically.
I don't disagree, but we had done it this way to be consistent with the other
get_xyz_gdbarch_data functions. With the switch to gdbarch_tdep<>, the function
is now removed anyway.
> > +get_intelgt_gdbarch_data (gdbarch *gdbarch)
> > +{
> > + intelgt_gdbarch_data *result = intelgt_gdbarch_data_handle.get (gdbarch);
> > + if (result == nullptr)
> > + result = intelgt_gdbarch_data_handle.emplace (gdbarch);
> > + return result;
> > +}
> > +
> > +/* 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, size_t size, gdb_byte *buffer,
>
> It's better to accept a gdb::array_view<gdb_byte> rather than a pointer
> and a size.
Done.
> > + 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 + size > regsize)
> > + error (_("%s[%ld:%ld] is outside the range of %s[%d:0]."),
> > + regname, (offset + size - 1), offset, regname, (regsize - 1));
> > +
> > + register_status reg_status
> > + = regcache->cooked_read_part (regnum, offset, size, 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 for the register listing in the target
> > + description to be in the same order in each regeset as the
> > + intended DWARF numbering order. Currently this is always
>
> Remove either "is" or "holds" from this sentence.
Done.
> > + holds true when gdbserver generates the target description. */
> > +
> > +static int
> > +intelgt_dwarf_reg_to_regnum (gdbarch *gdbarch, int num)
> > +{
>
> <snip>
>
> > +/* Frame unwinding. */
> > +
> > +static void
> > +intelgt_frame_this_id (const frame_info_ptr &this_frame,
> > + void **this_prologue_cache, frame_id *this_id)
> > +{
> > + /* FIXME: Other tdeps populate and use the cache. */
> > +
> > + /* Try to use symbol information to get the current start address. */
> > + CORE_ADDR func;
> > +
> > + if (get_frame_func_if_available (this_frame, &func))
> > + {
> > + /* Use the current PC as a fallback if no symbol info is available. */
> > + if (func == 0)
> > + func = get_frame_pc (this_frame);
> > +
> > + /* FIXME: Because there is no full notion of stack, it
> > + should be OK to ignore the SP reg. Currently, we cannot use SP
> > + even if we want to, because SP's size is 16 bytes whereas
> > + CORE_ADDR is 8. */
>
> Sorry, this made me curious: why is SP 16 bytes? Isn't 64-bit address
> space enough for anyone?
I apologize; this comment is wrong and is misleading. For unwinding, we use
dwarf sniffers. Assembly-level unwinding is not supported at the moment.
The 'intelgt_frame_this_id' function should read:
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;
}
In addition, the 'intelgt_frame_prev_register' function would be removed.
We'll include these changes in the next revision.
>
> > + *this_id = frame_id_build_unavailable_stack (func);
> > + }
> > + else
> > + *this_id = outer_frame_id;
> > +}
>
> --
> Thiago
Thanks again for your comments.
-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] 77+ messages in thread
end of thread, other threads:[~2024-11-08 13:56 UTC | newest]
Thread overview: 77+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-07-02 11:42 [PATCH 00/46] A new target to debug Intel GPUs Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 01/46] gdb, intelgt: add intelgt as a basic machine Tankut Baris Aktemur
2024-07-02 15:49 ` Maciej W. Rozycki
2024-07-03 8:01 ` Aktemur, Tankut Baris
2024-07-02 11:42 ` [PATCH 02/46] bfd: add intelgt target to BFD Tankut Baris Aktemur
2024-10-15 22:15 ` Thiago Jung Bauermann
2024-10-25 12:18 ` Aktemur, Tankut Baris
2024-07-02 11:42 ` [PATCH 03/46] ld: add intelgt as a target configuration Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 04/46] opcodes: add intelgt as a configuration Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 05/46] gdb, arch, intelgt: add intelgt arch definitions Tankut Baris Aktemur
2024-07-03 17:17 ` Guinevere Larsen
2024-07-04 6:47 ` Aktemur, Tankut Baris
2024-10-15 22:44 ` Thiago Jung Bauermann
2024-10-15 22:57 ` Thiago Jung Bauermann
2024-10-25 12:18 ` Aktemur, Tankut Baris
2024-07-02 11:42 ` [PATCH 06/46] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture Tankut Baris Aktemur
2024-08-26 16:27 ` Lancelot SIX
2024-08-29 13:09 ` Aktemur, Tankut Baris
2024-08-29 19:11 ` Lancelot SIX
2024-09-06 9:15 ` Aktemur, Tankut Baris
2024-09-30 15:35 ` Alexandra Petlanova Hajkova
2024-10-09 7:55 ` Aktemur, Tankut Baris
2024-10-15 23:35 ` Thiago Jung Bauermann
2024-11-08 13:56 ` Aktemur, Tankut Baris
2024-07-02 11:42 ` [PATCH 07/46] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description Tankut Baris Aktemur
2024-07-02 13:28 ` Eli Zaretskii
2024-07-02 11:42 ` [PATCH 08/46] gdb, intelgt: add disassemble feature for the Intel GT architecture Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 09/46] gdbsupport, filestuff, ze: temporary files Tankut Baris Aktemur
2024-08-31 0:11 ` Lancelot SIX
2024-09-06 14:52 ` Metzger, Markus T
2024-07-02 11:42 ` [PATCH 10/46] gdb, gdbserver, ze: in-memory libraries Tankut Baris Aktemur
2024-07-02 13:20 ` Eli Zaretskii
2024-07-02 11:42 ` [PATCH 11/46] gdb, gdbserver, rsp, ze: acknowledge libraries Tankut Baris Aktemur
2024-07-02 13:25 ` Eli Zaretskii
2024-07-03 7:19 ` Aktemur, Tankut Baris
2024-07-02 11:42 ` [PATCH 12/46] gdb, solib, ze: solib_bfd_open_from_target_memory Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 13/46] gdb, remote, ze: fix "$Hc-1#09...Packet received: E01" during startup Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 14/46] gdb, infrun, ze: allow saving process events Tankut Baris Aktemur
2024-08-31 18:00 ` Lancelot SIX
2024-09-09 14:33 ` Metzger, Markus T
2024-07-02 11:42 ` [PATCH 15/46] gdb, ze: add TARGET_WAITKIND_UNAVAILABLE Tankut Baris Aktemur
2024-09-04 10:47 ` Lancelot SIX
2024-09-10 7:47 ` Metzger, Markus T
2024-07-02 11:42 ` [PATCH 16/46] gdb, infrun, ze: handle stopping unavailable threads Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 17/46] gdb, infrun, ze: allow resuming " Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 18/46] gdb, gdbserver, ze: add U stop reply Tankut Baris Aktemur
2024-07-02 13:29 ` Eli Zaretskii
2024-07-02 11:42 ` [PATCH 19/46] gdb, gdbserver, ze: add library notification to " Tankut Baris Aktemur
2024-07-02 13:30 ` Eli Zaretskii
2024-07-02 11:42 ` [PATCH 20/46] gdbserver, ze: report TARGET_WAITKIND_UNAVAILABLE events Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 21/46] gdb, ze: handle TARGET_WAITKIND_UNAVAILABLE in stop_all_threads Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 22/46] gdb, remote: handle thread unavailability in print_one_stopped_thread Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 23/46] gdb, remote: do 'remote_add_inferior' in 'remote_notice_new_inferior' earlier Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 24/46] gdb, remote: handle a generic process PID in remote_notice_new_inferior Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 25/46] gdb, remote: handle a generic process PID in process_stop_reply Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 26/46] gdb: revise the pid_to_exec_file target op Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 27/46] gdb: use the pid from inferior in setup_inferior Tankut Baris Aktemur
2024-07-02 11:42 ` [PATCH 28/46] gdb: load solibs even when exec_bfd does not exist Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 29/46] gdbserver: import AC_LIB_HAVE_LINKFLAGS macro into the autoconf script Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 30/46] gdbserver: add a pointer to the owner thread in regcache Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 31/46] gdbserver: dump 'xx...x' in collect_register_as_string for unavailable register Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 32/46] gdbserver: wait for stopped threads in queue_stop_reply_callback Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 33/46] gdbserver: adjust pid after the target attaches Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 34/46] gdb: do not create a thread after a process event Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 35/46] gdb, ze: on a whole process stop, mark all threads as not_resumed Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 36/46] gdb, dwarf, ze: add DW_OP_INTEL_regval_bits Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 37/46] gdbserver, ze, intelgt: introduce ze-low and intel-ze-low targets Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 38/46] testsuite, sycl: add SYCL support Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 39/46] testsuite, sycl: add test for backtracing inside a kernel Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 40/46] testsuite, sycl: add test for 'info locals' and 'info args' Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 41/46] testsuite, sycl: add tests for stepping and accessing data elements Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 42/46] testsuite, sycl: add test for 1-D and 2-D parallel_for kernels Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 43/46] testsuite, sycl: add test for scheduler-locking Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 44/46] testsuite, arch, intelgt: add a disassembly test Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 45/46] testsuite, arch, intelgt: add intelgt-program-bp.exp Tankut Baris Aktemur
2024-07-02 11:43 ` [PATCH 46/46] testsuite, sycl: test canceling a stepping flow Tankut Baris Aktemur
2024-08-23 6:54 ` [PATCH 00/46] A new target to debug Intel GPUs 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;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).