* [PATCH v3 01/44] gdb, intelgt: add intelgt as a basic machine
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-12-09 20:44 ` Simon Marchi
2025-08-01 9:37 ` [PATCH v3 02/44] bfd: add intelgt target to BFD Tankut Baris Aktemur
` (45 subsequent siblings)
46 siblings, 1 reply; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
Add 'intelgt' as a basic machine to config.sub.
Note: This patch is already merged to config.sub upstream.
---
config.sub | 1 +
1 file changed, 1 insertion(+)
diff --git a/config.sub b/config.sub
index 4aaae46f6f74428949f32922308f48bd1316e7c1..664ee26124ac817257f076ed1d95f99cd0530fa4 100755
--- a/config.sub
+++ b/config.sub
@@ -1321,6 +1321,7 @@ case $cpu-$vendor in
| i960 \
| ia16 \
| ia64 \
+ | intelgt \
| ip2k \
| iq2000 \
| javascript \
--
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] 92+ messages in thread* Re: [PATCH v3 01/44] gdb, intelgt: add intelgt as a basic machine
2025-08-01 9:37 ` [PATCH v3 01/44] gdb, intelgt: add intelgt as a basic machine Tankut Baris Aktemur
@ 2025-12-09 20:44 ` Simon Marchi
2025-12-19 11:13 ` Aktemur, Tankut Baris
0 siblings, 1 reply; 92+ messages in thread
From: Simon Marchi @ 2025-12-09 20:44 UTC (permalink / raw)
To: Tankut Baris Aktemur, gdb-patches, Markus Metzger
On 8/1/25 5:37 AM, Tankut Baris Aktemur wrote:
> Add 'intelgt' as a basic machine to config.sub.
>
> Note: This patch is already merged to config.sub upstream.
I would suggest updating config.sub and config.guess the same way it has
been done historically, for instance like this:
https://gitlab.com/gnutools/binutils-gdb/-/commit/2c4b5f54b8432bb442f95748cd60fcdcc48f239b
i.e. don't bring in a single change, just sync the whole files with
upstream. Send a patch just for that, and it will be merged on its own.
For those who (like me before reviewing this patch) don't know where
these files come from, this is where:
https://cgit.git.savannah.gnu.org/cgit/config.git/
Simon
^ permalink raw reply [flat|nested] 92+ messages in thread
* RE: [PATCH v3 01/44] gdb, intelgt: add intelgt as a basic machine
2025-12-09 20:44 ` Simon Marchi
@ 2025-12-19 11:13 ` Aktemur, Tankut Baris
0 siblings, 0 replies; 92+ messages in thread
From: Aktemur, Tankut Baris @ 2025-12-19 11:13 UTC (permalink / raw)
To: Simon Marchi, gdb-patches, Metzger, Markus T
On Tuesday, December 9, 2025 9:45 PM, Simon Marchi wrote:
> On 8/1/25 5:37 AM, Tankut Baris Aktemur wrote:
> > Add 'intelgt' as a basic machine to config.sub.
> >
> > Note: This patch is already merged to config.sub upstream.
>
> I would suggest updating config.sub and config.guess the same way it has
> been done historically, for instance like this:
>
> https://gitlab.com/gnutools/binutils-gdb/-
> /commit/2c4b5f54b8432bb442f95748cd60fcdcc48f239b
>
> i.e. don't bring in a single change, just sync the whole files with
> upstream. Send a patch just for that, and it will be merged on its own.
>
> For those who (like me before reviewing this patch) don't know where
> these files come from, this is where:
>
> https://cgit.git.savannah.gnu.org/cgit/config.git/
>
> Simon
Submitted here:
https://sourceware.org/pipermail/gdb-patches/2025-December/223537.html
Thank you,
-Baris
Intel Deutschland GmbH
Registered Address: Dornacher Straße 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht München HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread
* [PATCH v3 02/44] bfd: add intelgt target to BFD
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 01/44] gdb, intelgt: add intelgt as a basic machine Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 12:20 ` Jan Beulich
2025-12-09 21:05 ` Simon Marchi
2025-08-01 9:37 ` [PATCH v3 03/44] ld: add intelgt as a target configuration Tankut Baris Aktemur
` (44 subsequent siblings)
46 siblings, 2 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger, binutils
From: Natalia Saiapova <natalia.saiapova@intel.com>
Add description of IntelGT target to BFD. Describe its relocation
types.
To: <binutils@sourceware.org>
---
bfd/Makefile.am | 2 +
bfd/Makefile.in | 4 ++
bfd/archures.c | 4 ++
bfd/bfd-in2.h | 6 ++
bfd/config.bfd | 13 +++-
bfd/configure | 1 +
bfd/configure.ac | 1 +
bfd/cpu-intelgt.c | 57 +++++++++++++++
bfd/elf64-intelgt.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++
bfd/libbfd.h | 2 +
bfd/reloc.c | 7 ++
bfd/targets.c | 2 +
binutils/readelf.c | 9 +++
include/elf/intelgt.h | 39 ++++++++++
14 files changed, 339 insertions(+), 3 deletions(-)
diff --git a/bfd/Makefile.am b/bfd/Makefile.am
index 3c3243269f12a603d2036d61c0c7a26db070d610..02b4f16e6efcd7d719c5c3e36ba7b1d8bd67db54 100644
--- a/bfd/Makefile.am
+++ b/bfd/Makefile.am
@@ -119,6 +119,7 @@ ALL_MACHINES = \
cpu-i386.lo \
cpu-ia64.lo \
cpu-iamcu.lo \
+ cpu-intelgt.lo \
cpu-ip2k.lo \
cpu-iq2000.lo \
cpu-kvx.lo \
@@ -202,6 +203,7 @@ ALL_MACHINES_CFILES = \
cpu-i386.c \
cpu-ia64.c \
cpu-iamcu.c \
+ cpu-intelgt.c \
cpu-ip2k.c \
cpu-iq2000.c \
cpu-kvx.c \
diff --git a/bfd/Makefile.in b/bfd/Makefile.in
index 4c259682fd0214f5df0ef8f5c2be867301a1650c..5c68934e79424d8171e7e1ea98543bb50dd06ad7 100644
--- a/bfd/Makefile.in
+++ b/bfd/Makefile.in
@@ -584,6 +584,7 @@ ALL_MACHINES = \
cpu-i386.lo \
cpu-ia64.lo \
cpu-iamcu.lo \
+ cpu-intelgt.lo \
cpu-ip2k.lo \
cpu-iq2000.lo \
cpu-kvx.lo \
@@ -667,6 +668,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 \
@@ -1021,6 +1023,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 \
@@ -1492,6 +1495,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 fd22c94d6d6e9dce0089b7be270c35fec4bce86c..25991565cb5ec4404f2026b10e375c4db5136804 100644
--- a/bfd/archures.c
+++ b/bfd/archures.c
@@ -399,6 +399,8 @@ DESCRIPTION
. bfd_arch_ia64, {* HP/Intel ia64. *}
.#define bfd_mach_ia64_elf64 64
.#define bfd_mach_ia64_elf32 32
+. bfd_arch_intelgt, {* Intel(R) Graphics Technology. *}
+.#define bfd_mach_intelgt 9
. bfd_arch_ip2k, {* Ubicom IP2K microcontrollers. *}
.#define bfd_mach_ip2022 1
.#define bfd_mach_ip2022ext 2
@@ -650,6 +652,7 @@ extern const bfd_arch_info_type bfd_hppa_arch;
extern const bfd_arch_info_type bfd_i386_arch;
extern const bfd_arch_info_type bfd_iamcu_arch;
extern const bfd_arch_info_type bfd_ia64_arch;
+extern const bfd_arch_info_type bfd_intelgt_arch;
extern const bfd_arch_info_type bfd_ip2k_arch;
extern const bfd_arch_info_type bfd_iq2000_arch;
extern const bfd_arch_info_type bfd_kvx_arch;
@@ -738,6 +741,7 @@ static const bfd_arch_info_type * const bfd_archures_list[] =
&bfd_i386_arch,
&bfd_iamcu_arch,
&bfd_ia64_arch,
+ &bfd_intelgt_arch,
&bfd_ip2k_arch,
&bfd_iq2000_arch,
&bfd_kvx_arch,
diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 2ff3e930bfa2fb441aa68bc81618747a576571a2..2133b126502955b92caae4fb23d2e98093aac72b 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -1620,6 +1620,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
@@ -7458,6 +7460,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
index eb20a01ef54aaeec6889bc010faa51576fe0d65b..edf2b83e00ef7f6b3edf2a0c0298db5b65115577 100644
--- a/bfd/config.bfd
+++ b/bfd/config.bfd
@@ -198,6 +198,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" ;;
@@ -705,12 +706,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
;;
@@ -781,7 +782,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 28ac5ccfbe570d7454db5ab81d67a4919f207878..3bbcb4f4833d66f4a9fc065712449d1bace9bc25 100755
--- a/bfd/configure
+++ b/bfd/configure
@@ -15573,6 +15573,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 502c526b606aaef61bead97db0f5ab83acf0aa1d..ddec7c895a596b1451155fe80ec5ab3783df8d15 100644
--- a/bfd/configure.ac
+++ b/bfd/configure.ac
@@ -497,6 +497,7 @@ do
ia64_elf64_hpux_be_vec) tb="$tb elf64-ia64.lo elfxx-ia64.lo elf64.lo $elf"; target_size=64 ;;
ia64_elf64_vms_vec) tb="$tb elf64-ia64-vms.lo elf64-ia64.lo elfxx-ia64.lo elf64.lo vms-lib.lo vms-misc.lo $elf"; target_size=64 ;;
ia64_pei_vec) tb="$tb pei-ia64.lo pepigen.lo $coff"; target_size=64 ;;
+ intelgt_elf64_vec) tb="$tb elf64-intelgt.lo elf64.lo $elf"; target_size=64 ;;
ip2k_elf32_vec) tb="$tb elf32-ip2k.lo elf32.lo $elf" ;;
iq2000_elf32_vec) tb="$tb elf32-iq2000.lo elf32.lo $elf" ;;
kvx_elf32_vec) tb="$tb elf32-kvx.lo elfxx-kvx.lo elf32.lo $elf $ipa" ;;
diff --git a/bfd/cpu-intelgt.c b/bfd/cpu-intelgt.c
new file mode 100644
index 0000000000000000000000000000000000000000..2d90e768325904dc5a265711a721f5437e13eb12
--- /dev/null
+++ b/bfd/cpu-intelgt.c
@@ -0,0 +1,57 @@
+/* BFD support for the Intel(R) Graphics Technology architecture.
+ Copyright (C) 2019-2025 Free Software Foundation, Inc.
+
+ This file is part of BFD, the Binary File Descriptor library.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#include "sysdep.h"
+#include "bfd.h"
+#include "libbfd.h"
+
+static void *
+bfd_arch_intelgt_fill (bfd_size_type count,
+ bool is_bigendian ATTRIBUTE_UNUSED,
+ bool code)
+{
+ void *fill = bfd_malloc (count);
+
+ if (fill != NULL)
+ {
+ /* nop on gen is 0x7e. */
+ memset (fill, code ? 0x7e : 0, count);
+ }
+
+ return fill;
+}
+
+const bfd_arch_info_type bfd_intelgt_arch =
+ {
+ 64, /* 64 bits in a word. */
+ 64, /* 64 bits in an address. */
+ 8, /* 8 bits in a byte. */
+ bfd_arch_intelgt, /* Architecture. */
+ bfd_mach_intelgt, /* Machine number. */
+ "intelgt", /* Architecture name. */
+ "intelgt", /* Printable name. */
+ 3, /* Section alignment power. */
+ true, /* Default machine for this architecture. */
+ bfd_default_compatible, /* Check for compatibility. */
+ bfd_default_scan, /* Check for an arch and mach hit. */
+ bfd_arch_intelgt_fill, /* Allocate and fill bfd. */
+ NULL, /* Pointer to next. */
+ 0 /* Maximum offset of a reloc from the start of an insn. */
+ };
diff --git a/bfd/elf64-intelgt.c b/bfd/elf64-intelgt.c
new file mode 100644
index 0000000000000000000000000000000000000000..41351318e797342d29fbafdf3802d09e91edadb6
--- /dev/null
+++ b/bfd/elf64-intelgt.c
@@ -0,0 +1,195 @@
+/* Intel(R) Graphics Technology-specific support for ELF
+ Copyright (C) 2022-2025 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 bab1f71f7e3ff6570e546085622b299be22bb8f5..70cf898f8379577a88af1f9682a6bb349b672636 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -3618,6 +3618,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 c9d53bb9e11b93d5394bd7e0bbd511a9852d550a..c7f2d80b02c53f4a2cf3b14c66b2c48e903b49f6 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -8266,6 +8266,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 c2ee9179f374cdc5686389c85c7cc2c18a0e260d..aed079c0a92564f8a765f3d1fceb6ad3745a06a9 100644
--- a/bfd/targets.c
+++ b/bfd/targets.c
@@ -762,6 +762,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;
@@ -1119,6 +1120,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 bb81c824ac3b5b98217e8ebc20c47bdda1c75561..f9612120352e2293c227a95b90c64b9955e7a882 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"
@@ -2395,6 +2396,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)
@@ -15589,6 +15594,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;
@@ -15728,6 +15735,8 @@ is_64bit_abs_reloc (Filedata * filedata, unsigned int reloc_type)
return reloc_type == 18; /* R_MIPS_64. */
case EM_KVX:
return reloc_type == 3; /* R_KVX_64 */
+ case EM_INTELGT:
+ return reloc_type == 1; /* R_ZE_SYM_ADDR. */
default:
return false;
}
diff --git a/include/elf/intelgt.h b/include/elf/intelgt.h
new file mode 100644
index 0000000000000000000000000000000000000000..cdade55ea4864aa23cd6e8b2a079e6f5e2ff91fb
--- /dev/null
+++ b/include/elf/intelgt.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2022-2025 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] 92+ messages in thread* Re: [PATCH v3 02/44] bfd: add intelgt target to BFD
2025-08-01 9:37 ` [PATCH v3 02/44] bfd: add intelgt target to BFD Tankut Baris Aktemur
@ 2025-08-01 12:20 ` Jan Beulich
2025-08-08 5:03 ` Metzger, Markus T
2025-12-09 21:05 ` Simon Marchi
1 sibling, 1 reply; 92+ messages in thread
From: Jan Beulich @ 2025-08-01 12:20 UTC (permalink / raw)
To: Tankut Baris Aktemur, Markus Metzger; +Cc: gdb-patches, binutils
On 01.08.2025 11:37, Tankut Baris Aktemur wrote:
> --- a/bfd/config.bfd
> +++ b/bfd/config.bfd
> @@ -198,6 +198,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" ;;
> @@ -705,12 +706,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
> ;;
I'm not convinced of forcing this as secondary target onto (about) everyone.
> @@ -781,7 +782,13 @@ case "${targ}" in
> targ_defvec=i386_elf32_vec
> targ_selvecs="i386_msdos_vec i386_aout_vec"
> ;;
> -
Please don't remove the (visually) separating blank lines; instead add a new
one yourself.
> +#ifdef BFD64
> + intelgt-*-elf)
> + targ_defvec=intelgt_elf64_vec
> + targ_selvecs="intelgt_elf64_vec"
Why would targ_selvecs need to hold what targ_defvec already has?
> --- /dev/null
> +++ b/bfd/elf64-intelgt.c
> @@ -0,0 +1,195 @@
> +/* Intel(R) Graphics Technology-specific support for ELF
> + Copyright (C) 2022-2025 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]))
What's wrong with ARRAY_SIZE()?
> +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 */
Based on the arch apparently using RELA relocations, src_mask shouldn't be
non-zero anywhere aiui.
> + MINUS_ONE, /* dst_mask */
While for a 64-bit reloc this looks right, ...
> + 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 */
... I don't think it's right here. I'm also unconvinced of the use of
complain_overflow_unsigned, when ...
> + 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 */
... the other half of the address can be represented this way.
> --- 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"
> @@ -2395,6 +2396,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)
> @@ -15589,6 +15594,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 */
Not sure here as well, when this can be the lower half of a 64-bit address.
> --- /dev/null
> +++ b/include/elf/intelgt.h
> @@ -0,0 +1,39 @@
> +/* Copyright (C) 2022-2025 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)
Why is this not R_ZE_PER_THREAD_PAYLOAD_OFFSET_32?
Jan
^ permalink raw reply [flat|nested] 92+ messages in thread* RE: [PATCH v3 02/44] bfd: add intelgt target to BFD
2025-08-01 12:20 ` Jan Beulich
@ 2025-08-08 5:03 ` Metzger, Markus T
0 siblings, 0 replies; 92+ messages in thread
From: Metzger, Markus T @ 2025-08-08 5:03 UTC (permalink / raw)
To: Beulich, Jan; +Cc: gdb-patches, binutils, Aktemur, Tankut Baris
Hello Jan,
Thanks for your review.
>> @@ -198,6 +198,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" ;;
>> @@ -705,12 +706,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
>> ;;
>
>I'm not convinced of forcing this as secondary target onto (about) everyone.
Indeed, this doesn't seem necessary. We'll remove that part.
>> +#ifdef BFD64
>> + intelgt-*-elf)
>> + targ_defvec=intelgt_elf64_vec
>> + targ_selvecs="intelgt_elf64_vec"
>
>Why would targ_selvecs need to hold what targ_defvec already has?
We'll remove that, too.
>> + 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 */
>
>Based on the arch apparently using RELA relocations, src_mask shouldn't be
>non-zero anywhere aiui.
We don't really need all this, I believe. We will drop the reloc map and howto
table in v4 and just keep the reloc numbers.
>> if (rtype == NULL)
>> @@ -15589,6 +15594,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 */
>
>Not sure here as well, when this can be the lower half of a 64-bit address.
We'll drop that, too.
>> +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)
>
>Why is this not R_ZE_PER_THREAD_PAYLOAD_OFFSET_32?
That's a typo. I'm adding a few to v4, all with ZE prefix.
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] 92+ messages in thread
* Re: [PATCH v3 02/44] bfd: add intelgt target to BFD
2025-08-01 9:37 ` [PATCH v3 02/44] bfd: add intelgt target to BFD Tankut Baris Aktemur
2025-08-01 12:20 ` Jan Beulich
@ 2025-12-09 21:05 ` Simon Marchi
2025-12-19 12:46 ` Aktemur, Tankut Baris
1 sibling, 1 reply; 92+ messages in thread
From: Simon Marchi @ 2025-12-09 21:05 UTC (permalink / raw)
To: Tankut Baris Aktemur, gdb-patches, Markus Metzger, binutils
On 8/1/25 5:37 AM, Tankut Baris Aktemur wrote:
> From: Natalia Saiapova <natalia.saiapova@intel.com>
>
> Add description of IntelGT target to BFD. Describe its relocation
> types.
>
> To: <binutils@sourceware.org>
Not a binutils maintainer, but I gave it a high level look.
> @@ -705,12 +706,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"
Does it makes sense to automatically enable intelgt support alongside
x86-64? Are they logically related, or completely different
architectures? Since it's possible to enale intelgt specifically (the
hunk below in the same file), I'm not sure why it should piggy back on
x86-64 here.
> diff --git a/include/elf/intelgt.h b/include/elf/intelgt.h
> new file mode 100644
> index 0000000000000000000000000000000000000000..cdade55ea4864aa23cd6e8b2a079e6f5e2ff91fb
> --- /dev/null
> +++ b/include/elf/intelgt.h
> @@ -0,0 +1,39 @@
> +/* Copyright (C) 2022-2025 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_
Based on the other files in this directory, the include guard should be
"_ELF_INTELGT_H".
Simon
^ permalink raw reply [flat|nested] 92+ messages in thread* RE: [PATCH v3 02/44] bfd: add intelgt target to BFD
2025-12-09 21:05 ` Simon Marchi
@ 2025-12-19 12:46 ` Aktemur, Tankut Baris
0 siblings, 0 replies; 92+ messages in thread
From: Aktemur, Tankut Baris @ 2025-12-19 12:46 UTC (permalink / raw)
To: Simon Marchi, gdb-patches, Metzger, Markus T, binutils
On Tuesday, December 9, 2025 10:05 PM, Simon Marchi wrote:
> On 8/1/25 5:37 AM, Tankut Baris Aktemur wrote:
> > From: Natalia Saiapova <natalia.saiapova@intel.com>
> >
> > Add description of IntelGT target to BFD. Describe its relocation
> > types.
> >
> > To: <binutils@sourceware.org>
>
> Not a binutils maintainer, but I gave it a high level look.
>
> > @@ -705,12 +706,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"
>
> Does it makes sense to automatically enable intelgt support alongside
> x86-64? Are they logically related, or completely different
> architectures? Since it's possible to enale intelgt specifically (the
> hunk below in the same file), I'm not sure why it should piggy back on
> x86-64 here.
Right, Jan Beulich had given the same comment, too. We remove these in the next
revision.
> > diff --git a/include/elf/intelgt.h b/include/elf/intelgt.h
> > new file mode 100644
> > index
> 0000000000000000000000000000000000000000..cdade55ea4864aa23cd6e8b2a079e6
> f5e2ff91fb
> > --- /dev/null
> > +++ b/include/elf/intelgt.h
> > @@ -0,0 +1,39 @@
> > +/* Copyright (C) 2022-2025 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_
>
> Based on the other files in this directory, the include guard should be
> "_ELF_INTELGT_H".
Fixed.
Thank you,
-Baris
Intel Deutschland GmbH
Registered Address: Dornacher Straße 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht München HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread
* [PATCH v3 03/44] ld: add intelgt as a target configuration
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 01/44] gdb, intelgt: add intelgt as a basic machine Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 02/44] bfd: add intelgt target to BFD Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 12:06 ` Jan Beulich
2025-08-01 9:37 ` [PATCH v3 04/44] opcodes: add intelgt as a configuration Tankut Baris Aktemur
` (43 subsequent siblings)
46 siblings, 1 reply; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger, binutils
Add 'intelgt-*-elf' as a target configuration for ld.
To: <binutils@sourceware.org>
---
ld/configure.tgt | 2 ++
1 file changed, 2 insertions(+)
diff --git a/ld/configure.tgt b/ld/configure.tgt
index 3f49485ae9f072490ed4839cd8d9007962abe189..172a4c13441b8a6889257d7f6f5ff48bcf2e5fc8 100644
--- a/ld/configure.tgt
+++ b/ld/configure.tgt
@@ -460,6 +460,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] 92+ messages in thread* Re: [PATCH v3 03/44] ld: add intelgt as a target configuration
2025-08-01 9:37 ` [PATCH v3 03/44] ld: add intelgt as a target configuration Tankut Baris Aktemur
@ 2025-08-01 12:06 ` Jan Beulich
2025-08-08 5:03 ` Metzger, Markus T
0 siblings, 1 reply; 92+ messages in thread
From: Jan Beulich @ 2025-08-01 12:06 UTC (permalink / raw)
To: Tankut Baris Aktemur, Markus Metzger; +Cc: gdb-patches, binutils
On 01.08.2025 11:37, Tankut Baris Aktemur wrote:
> Add 'intelgt-*-elf' as a target configuration for ld.
>
> To: <binutils@sourceware.org>
> ---
> ld/configure.tgt | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/ld/configure.tgt b/ld/configure.tgt
> index 3f49485ae9f072490ed4839cd8d9007962abe189..172a4c13441b8a6889257d7f6f5ff48bcf2e5fc8 100644
> --- a/ld/configure.tgt
> +++ b/ld/configure.tgt
> @@ -460,6 +460,8 @@ ia64-*-*vms*) targ_emul=elf64_ia64_vms
> ;;
> ia64-*-aix*) targ_emul=elf64_aix
> ;;
> +intelgt-*-elf) targ_emul=elf_x86_64
> + ;;
How does this fit with patch 02 introducing intelgt as a separate arch? How
useful is it anyway to have a working(?) linker when there's no assembler?
Jan
^ permalink raw reply [flat|nested] 92+ messages in thread
* RE: [PATCH v3 03/44] ld: add intelgt as a target configuration
2025-08-01 12:06 ` Jan Beulich
@ 2025-08-08 5:03 ` Metzger, Markus T
0 siblings, 0 replies; 92+ messages in thread
From: Metzger, Markus T @ 2025-08-08 5:03 UTC (permalink / raw)
To: Beulich, Jan; +Cc: gdb-patches, binutils, Aktemur, Tankut Baris
Thanks, Jan,
>> --- a/ld/configure.tgt
>> +++ b/ld/configure.tgt
>> @@ -460,6 +460,8 @@ ia64-*-*vms*)
> targ_emul=elf64_ia64_vms
>> ;;
>> ia64-*-aix*) targ_emul=elf64_aix
>> ;;
>> +intelgt-*-elf) targ_emul=elf_x86_64
>> + ;;
>
>How does this fit with patch 02 introducing intelgt as a separate arch? How
>useful is it anyway to have a working(?) linker when there's no assembler?
This isn't necessary. We'll drop that patch.
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] 92+ messages in thread
* [PATCH v3 04/44] opcodes: add intelgt as a configuration
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (2 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 03/44] ld: add intelgt as a target configuration Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 05/44] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description Tankut Baris Aktemur
` (42 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger, binutils
Intel GT does not use the opcodes lib for disassembling instructions.
Therefore, add the simplest configuration to satisfy building without
errors.
To: <binutils@sourceware.org>
---
opcodes/configure | 1 +
opcodes/configure.ac | 1 +
2 files changed, 2 insertions(+)
diff --git a/opcodes/configure b/opcodes/configure
index 7dc7b49ca2b5d3879ace29c460effa7fbb35efc3..b21982562ddef04e3f21437caf537e99459df5b2 100755
--- a/opcodes/configure
+++ b/opcodes/configure
@@ -13972,6 +13972,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 783ac2a7a7a874f48b9f5c3158693db9a410c035..ff1f4ea329690a01966b125f7b13f5de21a6dd70 100644
--- a/opcodes/configure.ac
+++ b/opcodes/configure.ac
@@ -285,6 +285,7 @@ if test x${all_targets} = xfalse ; then
bfd_i386_arch|bfd_iamcu_arch)
ta="$ta i386-dis.lo" ;;
bfd_ia64_arch) ta="$ta ia64-dis.lo ia64-opc.lo" ;;
+ bfd_intelgt_arch) ;;
bfd_ip2k_arch) ta="$ta ip2k-asm.lo ip2k-desc.lo ip2k-dis.lo ip2k-ibld.lo ip2k-opc.lo" using_cgen=yes ;;
bfd_epiphany_arch) ta="$ta epiphany-asm.lo epiphany-desc.lo epiphany-dis.lo epiphany-ibld.lo epiphany-opc.lo" using_cgen=yes ;;
bfd_iq2000_arch) ta="$ta iq2000-asm.lo iq2000-desc.lo iq2000-dis.lo iq2000-ibld.lo iq2000-opc.lo" using_cgen=yes ;;
--
2.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] 92+ messages in thread* [PATCH v3 05/44] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (3 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 04/44] opcodes: add intelgt as a configuration Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-12-09 21:27 ` Simon Marchi
2025-08-01 9:37 ` [PATCH v3 06/44] gdb, arch, intelgt: add intelgt arch definitions Tankut Baris Aktemur
` (41 subsequent siblings)
46 siblings, 1 reply; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
From: Nils-Christian Kempke <nils-christian.kempke@intel.com>
Add <device> tag to the XML target description. The tag is a list of
device attributes. The extension enables passing device information
within the target description XML sent from gdbserver to gdb.
Co-authored-by: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
gdb/NEWS | 6 +++
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 | 48 ++++++++++++++++++++++++
gdbsupport/tdesc.h | 90 +++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 301 insertions(+), 1 deletion(-)
diff --git a/gdb/NEWS b/gdb/NEWS
index 4c9aed421bb05e97505faa37749952d93ba20e99..5736565d4758a31ad7902905d280c2f68f7a8b08 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -209,6 +209,12 @@ vFile:stat
lstat rather than stat. This has now been corrected. The
documentation has also been clarified.
+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.
+
* MI changes
** The =library-unloaded event now includes the 'ranges' field, which
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 2bbaf14c58473126fb1156173040fd9907289536..3d5b4491f775866d806782a92e0aa76c3539ff25 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -48819,6 +48819,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
@@ -48916,6 +48917,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 2ecc41eb1fa28c928a89ab6b6956721c7cf39d66..64ec387f18d0c2d287cd5b6a479431918dd5258e 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 dc5fbacfebd11a4a653cce9037e0f524552a74db..046d6484f7bfffcef8867e7aa4b7e2ad45b23ed1 100644
--- a/gdb/target-descriptions.c
+++ b/gdb/target-descriptions.c
@@ -359,6 +359,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;
@@ -644,6 +647,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 2f213dc973d57550bd5c7a9ad5e9e9b6b622679d..f8a3218c41ad563655a6eec12acc506859aa6ee5 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 start of a <device> element and its value. */
+
+static void
+tdesc_start_device (struct gdb_xml_parser *parser,
+ const gdb_xml_element *element,
+ void *user_data, std::vector<gdb_xml_value> &attributes)
+{
+ tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+ tdesc_device *device = new tdesc_device ();
+
+ gdb_xml_value *attr;
+
+ attr = xml_find_attribute (attributes, "vendor-id");
+ if (attr != nullptr)
+ device->vendor_id = * (ULONGEST *) attr->value.get ();
+
+ attr = xml_find_attribute (attributes, "target-id");
+ if (attr != nullptr)
+ device->target_id = * (ULONGEST *) attr->value.get ();
+
+ attr = xml_find_attribute (attributes, "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 54c105c54eca11b5dfd30a74260ec8af5de3417e..394263cc58e016187988247db16826b0bf62f5e9 100644
--- a/gdbserver/tdesc.cc
+++ b/gdbserver/tdesc.cc
@@ -190,6 +190,22 @@ set_tdesc_osabi (struct target_desc *target_desc, enum gdb_osabi osabi)
/* See gdbsupport/tdesc.h. */
+void
+set_tdesc_device_info (target_desc *target_desc, tdesc_device *device)
+{
+ target_desc->device.reset (device);
+}
+
+/* See gdbsupport/tdesc.h. */
+
+const tdesc_device *
+tdesc_device_info (const target_desc *target_desc)
+{
+ return target_desc->device.get ();
+}
+
+/* See gdbsupport/tdesc.h. */
+
const char *
tdesc_get_features_xml (const target_desc *tdesc)
{
diff --git a/gdbserver/tdesc.h b/gdbserver/tdesc.h
index 58b2395aaa07fe95b39ee1d5bb19c9d155661b78..a31f74d54d3bb296649bb8cf823174e06327e61b 100644
--- a/gdbserver/tdesc.h
+++ b/gdbserver/tdesc.h
@@ -60,6 +60,9 @@ struct target_desc final : tdesc_element
/* The value of <osabi> element in the XML, replying GDB. */
gdb::unique_xmalloc_ptr<char> osabi;
+ /* The value of the <device> element in the XML, replying GDB. */
+ tdesc_device_up device;
+
public:
target_desc ()
: registers_size (0)
diff --git a/gdbsupport/tdesc.cc b/gdbsupport/tdesc.cc
index ed92c864732e61fa12c4ebae85454c29e0ba9b3d..08438131b621ce9a55b95a50f25592f032b2de4a 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,52 @@ void print_xml_feature::visit_post (const target_desc *e)
add_line ("</target>");
}
+void
+print_xml_feature::visit (const tdesc_device *device)
+{
+ if (device == nullptr)
+ return;
+
+ std::string tmp = "<device";
+ if (device->vendor_id.has_value ())
+ string_appendf (tmp, " vendor-id=\"0x%04" PRIx32 "\"",
+ *device->vendor_id);
+
+ if (device->target_id.has_value ())
+ string_appendf (tmp, " target-id=\"0x%04" PRIx32 "\"",
+ *device->target_id);
+
+ if (!device->family.empty ())
+ string_appendf (tmp, " family=\"%s\"", device->family.c_str ());
+
+ if (!device->model.empty ())
+ string_appendf (tmp, " model=\"%s\"", device->model.c_str ());
+
+ if (device->stepping.has_value ())
+ string_appendf (tmp, " stepping=\"%d\"", *device->stepping);
+
+ if (!device->name.empty ())
+ string_appendf (tmp, " name=\"%s\"", device->name.c_str ());
+
+ if (!device->pci_slot.empty ())
+ string_appendf (tmp, " pci-slot=\"%s\"", device->pci_slot.c_str ());
+
+ if (!device->uuid.empty ())
+ 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 6bf66ad3dcdcb9148fddf3a462bad2c0ce549ab7..cec01df91ed4d40c35c799c8d8932f52be9207dd 100644
--- a/gdbsupport/tdesc.h
+++ b/gdbsupport/tdesc.h
@@ -26,6 +26,7 @@ struct tdesc_type_builtin;
struct tdesc_type_vector;
struct tdesc_type_with_fields;
struct tdesc_reg;
+struct tdesc_device;
struct target_desc;
/* The interface to visit different elements of target description. */
@@ -56,6 +57,9 @@ class tdesc_element_visitor
virtual void visit (const tdesc_reg *e)
{}
+
+ virtual void visit (const tdesc_device *e)
+ {}
};
class tdesc_element
@@ -315,6 +319,84 @@ struct tdesc_feature : tdesc_element
typedef std::unique_ptr<tdesc_feature> tdesc_feature_up;
+/* The device information in a target description. */
+
+struct tdesc_device : tdesc_element
+{
+ tdesc_device ()
+ {
+ family = "";
+ model = "";
+ name = "";
+ pci_slot = "";
+ uuid = "";
+ }
+
+ virtual ~tdesc_device () = default;
+
+ DISABLE_COPY_AND_ASSIGN (tdesc_device);
+
+ /* The id of the device vendor. */
+ std::optional<uint32_t> vendor_id;
+
+ /* The id of the device, given by its vendor. */
+ std::optional<uint32_t> target_id;
+
+ /* The family of the device. */
+ std::string family;
+
+ /* The model of the device. */
+ std::string model;
+
+ /* The stepping of the device. */
+ std::optional<uint32_t> stepping;
+
+ /* The name of the device. */
+ std::string name;
+
+ /* The location where the device is positioned. */
+ std::string pci_slot;
+
+ /* The UUID of the device. */
+ std::string uuid;
+
+ /* The number of cores available in the device. */
+ std::optional<size_t> total_cores;
+
+ /* The number of threads available in the device. */
+ std::optional<size_t> total_threads;
+
+ /* The subdevice id, if this device is a subdevice. */
+ std::optional<uint32_t> subdevice_id;
+
+ void accept (tdesc_element_visitor &v) const override
+ {
+ v.visit (this);
+ }
+
+ bool operator== (const tdesc_device &other) const
+ {
+ return (vendor_id == other.vendor_id
+ && target_id == other.target_id
+ && family == other.family
+ && model == other.model
+ && stepping == other.stepping
+ && name == other.name
+ && pci_slot == other.pci_slot
+ && uuid == other.uuid
+ && total_cores == other.total_cores
+ && total_threads == other.total_threads
+ && subdevice_id == other.subdevice_id);
+ }
+
+ bool operator!= (const tdesc_device &other) const
+ {
+ return !(*this == other);
+ }
+};
+
+typedef std::unique_ptr<tdesc_device> tdesc_device_up;
+
/* A deleter adapter for a target_desc. There are different
implementations of this deleter class in gdb and gdbserver because even
though the target_desc name is shared between the two projects, the
@@ -347,6 +429,13 @@ void set_tdesc_osabi (target_desc *target_desc, enum gdb_osabi osabi);
or NULL if no osabi was specified. */
const char *tdesc_osabi_name (const struct target_desc *target_desc);
+/* Set TARGET_DESC's device information. */
+void set_tdesc_device_info (target_desc *target_desc, tdesc_device *device);
+
+/* Return the device information associated with this target
+ description or NULL if device info does not exist. */
+const tdesc_device *tdesc_device_info (const target_desc *target_desc);
+
/* Return the type associated with ID in the context of FEATURE, or
NULL if none. */
struct tdesc_type *tdesc_named_type (const struct tdesc_feature *feature,
@@ -439,6 +528,7 @@ class print_xml_feature : public tdesc_element_visitor
void visit (const tdesc_type_vector *type) override;
void visit (const tdesc_type_with_fields *type) override;
void visit (const tdesc_reg *reg) override;
+ void visit (const tdesc_device *device) override;
private:
--
2.34.1
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread* Re: [PATCH v3 05/44] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description
2025-08-01 9:37 ` [PATCH v3 05/44] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description Tankut Baris Aktemur
@ 2025-12-09 21:27 ` Simon Marchi
2025-12-15 21:03 ` Simon Marchi
0 siblings, 1 reply; 92+ messages in thread
From: Simon Marchi @ 2025-12-09 21:27 UTC (permalink / raw)
To: Tankut Baris Aktemur, gdb-patches, Markus Metzger
On 8/1/25 5:37 AM, Tankut Baris Aktemur wrote:
> 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.
The dtd change allows a single <device> in the target description. When
(and if) you do multi-device debugging, you connect one inferior per
device?
From the ROCm perspective: if we ever want to support remote debugging
(no concrete plans, but we want to keep the door open to it in the
design) and we end up using this attribute, I suppose we'll want to be
able to use multiple <device> tags, since we do everything in a single
inferior. If so, it would be easier to support that possibility from
the start.
I think that the concept of "device" here corresponds to the concept of
"agent" on our side:
https://rocm.docs.amd.com/projects/ROCdbgapi/en/latest/doxygen/html/group__agent__group.html
I don't think the naming in the remote protocol matters that much, as
long as it's clear and consistent (and the right concepts are present).
I don't have comments on the code itself, seems fine to me.
Simon
^ permalink raw reply [flat|nested] 92+ messages in thread
* Re: [PATCH v3 05/44] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description
2025-12-09 21:27 ` Simon Marchi
@ 2025-12-15 21:03 ` Simon Marchi
2025-12-18 15:04 ` Aktemur, Tankut Baris
0 siblings, 1 reply; 92+ messages in thread
From: Simon Marchi @ 2025-12-15 21:03 UTC (permalink / raw)
To: Tankut Baris Aktemur, gdb-patches, Markus Metzger
On 12/9/25 4:27 PM, Simon Marchi wrote:
> On 8/1/25 5:37 AM, Tankut Baris Aktemur wrote:
>> 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.
>
> The dtd change allows a single <device> in the target description. When
> (and if) you do multi-device debugging, you connect one inferior per
> device?
>
> From the ROCm perspective: if we ever want to support remote debugging
> (no concrete plans, but we want to keep the door open to it in the
> design) and we end up using this attribute, I suppose we'll want to be
> able to use multiple <device> tags, since we do everything in a single
> inferior. If so, it would be easier to support that possibility from
> the start.
I talked about that with my team, and what I proposed doesn't make
sense. The entire target description describes just one arch, it
wouldn't make sense to have multiple devices of possibly different
arches in it.
In ROCgdb, we have an "info agents" command, which lists the agents
(devices) available to the current inferior (process). You can, for
instance, stop at main(), and then type "info agents" to get the list.
Therefore, I presume that we would instead need a new kind of query,
"get me the list of devices", and that query could re-use the XML device
format proposed in this patch.
Another question I have is whether the low-level details specific to one
device should really belong there in the target description. In other
words, imagine that you debug with two identical devices simultaneously,
should the target descriptions for both devices be identical? I tend to
think that they should. Perhaps things like pci_slot shouldn't be in
the target description, but in a separate "device" concept outside
target descriptions. Things like vendor_id and target_id allow
inferring details about the architecture, so I understand why it would
be in there.
Simon
^ permalink raw reply [flat|nested] 92+ messages in thread
* RE: [PATCH v3 05/44] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description
2025-12-15 21:03 ` Simon Marchi
@ 2025-12-18 15:04 ` Aktemur, Tankut Baris
2026-01-09 19:12 ` Aktemur, Tankut Baris
0 siblings, 1 reply; 92+ messages in thread
From: Aktemur, Tankut Baris @ 2025-12-18 15:04 UTC (permalink / raw)
To: Simon Marchi, gdb-patches, Metzger, Markus T
Hi Simon,
On Monday, December 15, 2025 10:04 PM, Simon Marchi wrote:
> On 12/9/25 4:27 PM, Simon Marchi wrote:
> > On 8/1/25 5:37 AM, Tankut Baris Aktemur wrote:
> >> 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.
> >
> > The dtd change allows a single <device> in the target description.
> When
> > (and if) you do multi-device debugging, you connect one inferior per
> > device?
Yes, we represent each device as a separate inferior.
> > From the ROCm perspective: if we ever want to support remote debugging
> > (no concrete plans, but we want to keep the door open to it in the
> > design) and we end up using this attribute, I suppose we'll want to be
> > able to use multiple <device> tags, since we do everything in a single
> > inferior. If so, it would be easier to support that possibility from
> > the start.
>
> I talked about that with my team, and what I proposed doesn't make
> sense. The entire target description describes just one arch, it
> wouldn't make sense to have multiple devices of possibly different
> arches in it.
>
> In ROCgdb, we have an "info agents" command, which lists the agents
> (devices) available to the current inferior (process). You can, for
> instance, stop at main(), and then type "info agents" to get the list.
> Therefore, I presume that we would instead need a new kind of query,
> "get me the list of devices", and that query could re-use the XML device
> format proposed in this patch.
I agree, this sounds reasonable to me.
> Another question I have is whether the low-level details specific to one
> device should really belong there in the target description. In other
> words, imagine that you debug with two identical devices simultaneously,
> should the target descriptions for both devices be identical? I tend to
> think that they should. Perhaps things like pci_slot shouldn't be in
> the target description, but in a separate "device" concept outside
> target descriptions. Things like vendor_id and target_id allow
> inferring details about the architecture, so I understand why it would
> be in there.
>
> Simon
In our downstream debugger, we have an "info devices" command that lists
the properties of the devices as obtained from this XML information.
vendor_id and target_id are necessary, like you say, so that the target
description can validate it supports that device and can do device-specific
work, if necessary. Other information is there for informing the user in the
"info devices" command. The pci_slot is useful, because if the system has
multiple identical devices, pci_slot becomes the only distinguishing property.
Although it is not a built-in property of the device (like number of cores),
it is still physical&static -- you wouldn’t/couldn't change the pci_slot
of a device during the debug session. In that sense, it's a bit in the grey
area.
Is pci_slot the only field that concerns you? We can remove it from the patch
and consider later.
Btw, at some point we should also align on the "info agents" vs. "info devices"
to provide the users with a uniform presentation, but it's not on the critical
path.
-Baris
Intel Deutschland GmbH
Registered Address: Dornacher Straße 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht München HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread
* RE: [PATCH v3 05/44] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description
2025-12-18 15:04 ` Aktemur, Tankut Baris
@ 2026-01-09 19:12 ` Aktemur, Tankut Baris
2026-01-09 19:34 ` Simon Marchi
0 siblings, 1 reply; 92+ messages in thread
From: Aktemur, Tankut Baris @ 2026-01-09 19:12 UTC (permalink / raw)
To: Simon Marchi, gdb-patches, Metzger, Markus T
Hi Simon,
On Thursday, December 18, 2025 4:04 PM, Aktemur, Tankut Baris wrote:
> Hi Simon,
>
> On Monday, December 15, 2025 10:04 PM, Simon Marchi wrote:
> > On 12/9/25 4:27 PM, Simon Marchi wrote:
> > > On 8/1/25 5:37 AM, Tankut Baris Aktemur wrote:
> > >> 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.
> > >
> > > The dtd change allows a single <device> in the target description.
> > When
> > > (and if) you do multi-device debugging, you connect one inferior per
> > > device?
>
> Yes, we represent each device as a separate inferior.
>
> > > From the ROCm perspective: if we ever want to support remote
> debugging
> > > (no concrete plans, but we want to keep the door open to it in the
> > > design) and we end up using this attribute, I suppose we'll want to
> be
> > > able to use multiple <device> tags, since we do everything in a
> single
> > > inferior. If so, it would be easier to support that possibility
> from
> > > the start.
> >
> > I talked about that with my team, and what I proposed doesn't make
> > sense. The entire target description describes just one arch, it
> > wouldn't make sense to have multiple devices of possibly different
> > arches in it.
> >
> > In ROCgdb, we have an "info agents" command, which lists the agents
> > (devices) available to the current inferior (process). You can, for
> > instance, stop at main(), and then type "info agents" to get the list.
> > Therefore, I presume that we would instead need a new kind of query,
> > "get me the list of devices", and that query could re-use the XML
> device
> > format proposed in this patch.
>
> I agree, this sounds reasonable to me.
>
> > Another question I have is whether the low-level details specific to
> one
> > device should really belong there in the target description. In other
> > words, imagine that you debug with two identical devices
> simultaneously,
> > should the target descriptions for both devices be identical? I tend
> to
> > think that they should. Perhaps things like pci_slot shouldn't be in
> > the target description, but in a separate "device" concept outside
> > target descriptions. Things like vendor_id and target_id allow
> > inferring details about the architecture, so I understand why it would
> > be in there.
> >
> > Simon
>
> In our downstream debugger, we have an "info devices" command that lists
> the properties of the devices as obtained from this XML information.
> vendor_id and target_id are necessary, like you say, so that the target
> description can validate it supports that device and can do device-
> specific
> work, if necessary. Other information is there for informing the user
> in the
> "info devices" command. The pci_slot is useful, because if the system
> has
> multiple identical devices, pci_slot becomes the only distinguishing
> property.
> Although it is not a built-in property of the device (like number of
> cores),
> it is still physical&static -- you wouldn’t/couldn't change the pci_slot
> of a device during the debug session. In that sense, it's a bit in the
> grey
> area.
>
> Is pci_slot the only field that concerns you? We can remove it from the
> patch
> and consider later.
Just pinging for this question.
Thanks,
-Baris
Intel Deutschland GmbH
Registered Address: Dornacher Straße 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht München HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread
* Re: [PATCH v3 05/44] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description
2026-01-09 19:12 ` Aktemur, Tankut Baris
@ 2026-01-09 19:34 ` Simon Marchi
0 siblings, 0 replies; 92+ messages in thread
From: Simon Marchi @ 2026-01-09 19:34 UTC (permalink / raw)
To: Aktemur, Tankut Baris, gdb-patches, Metzger, Markus T
On 1/9/26 2:12 PM, Aktemur, Tankut Baris wrote:
>> In our downstream debugger, we have an "info devices" command that lists
>> the properties of the devices as obtained from this XML information.
>> vendor_id and target_id are necessary, like you say, so that the target
>> description can validate it supports that device and can do device-
>> specific
>> work, if necessary. Other information is there for informing the user
>> in the
>> "info devices" command. The pci_slot is useful, because if the system
>> has
>> multiple identical devices, pci_slot becomes the only distinguishing
>> property.
>> Although it is not a built-in property of the device (like number of
>> cores),
>> it is still physical&static -- you wouldn’t/couldn't change the pci_slot
>> of a device during the debug session. In that sense, it's a bit in the
>> grey
>> area.
>>
>> Is pci_slot the only field that concerns you? We can remove it from the
>> patch
>> and consider later.
>
> Just pinging for this question.
I think that pci_slot is the only one, yes. I view a target description
or a gdbarch as describing a "class" of computation unit
(CPU/GPU/whatever), not one specific instance of it. If you add
pci_slot, then the target description / gdbarch now describes one
specific instance, instead of the whole class.
That's why I tend to think that "devices" should perhaps be a concept of
its own. Two identical devices would share ("point to") the same target
description.
We just need to agree on what a target description fundamentally is, I
haven't thought about it more than that.
Simon
^ permalink raw reply [flat|nested] 92+ messages in thread
* [PATCH v3 06/44] gdb, arch, intelgt: add intelgt arch definitions
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (4 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 05/44] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-12-09 21:48 ` Simon Marchi
2025-08-01 9:37 ` [PATCH v3 07/44] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture Tankut Baris Aktemur
` (40 subsequent siblings)
46 siblings, 1 reply; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
From: Markus Metzger <markus.t.metzger@intel.com>
Provide Intel GT architecture-specific definitions that can be used by
both the low target at the server side and tdep at the GDB side.
Other than, for example, IA, Intel GT does not have a dedicated
breakpoint instruction. Instead, it has a breakpoint bit in each
instruction. We define arch methods for dealing with instruction
breakpoint bits.
Co-authored-by: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Co-authored-by: Mihails Strasuns <mihails.strasuns@intel.com>
Co-authored-by: Natalia Saiapova <natalia.saiapova@intel.com>
Reviewed-By: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
---
gdb/Makefile.in | 1 +
gdb/arch/intelgt.c | 191 +++++++++++++++++++++++++++++++++++++++++++++++++++++
gdb/arch/intelgt.h | 186 +++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 378 insertions(+)
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index fc0c56564c232523b5974a128da6bf58c6bb3160..8aac71d7057980a45e3cb89be2c583223762a1c7 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -756,6 +756,7 @@ ALL_64_TARGET_OBS = \
arch/aarch64-scalable-linux.o \
arch/amd64-linux-tdesc.o \
arch/amd64.o \
+ arch/intelgt.o \
arch/riscv.o \
bpf-tdep.o \
ia64-linux-tdep.o \
diff --git a/gdb/arch/intelgt.c b/gdb/arch/intelgt.c
new file mode 100644
index 0000000000000000000000000000000000000000..a86209ffa8369fd3693b6db0b51cb6f9b84fe622
--- /dev/null
+++ b/gdb/arch/intelgt.c
@@ -0,0 +1,191 @@
+/* Copyright (C) 2019-2025 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "intelgt.h"
+#include <stdlib.h>
+
+namespace intelgt {
+
+/* Get the bit at POS in INST. */
+
+bool
+get_inst_bit (gdb::array_view<const gdb_byte> inst, int pos)
+{
+ if (pos < 0 || (inst.size () * 8) <= pos)
+ internal_error (_("bad bit offset: %d"), pos);
+
+ const int idx = pos >> 3;
+ const int off = pos & 7;
+ const int mask = 1 << off;
+ const gdb_byte byte = inst[idx];
+
+ return (byte & mask) != 0;
+}
+
+/* Set the bit at POS in INST. */
+
+bool
+set_inst_bit (gdb::array_view<gdb_byte> inst, int pos)
+{
+ if (pos < 0 || (inst.size () * 8) <= pos)
+ internal_error (_("bad bit offset: %d"), pos);
+
+ const int idx = pos >> 3;
+ const int off = pos & 7;
+ const int mask = 1 << off;
+ const gdb_byte byte = inst[idx];
+
+ const bool old = (byte & mask) != 0;
+ inst[idx] |= mask;
+
+ return old;
+}
+
+/* Clear the bit at POS in INST. */
+
+bool
+clear_inst_bit (gdb::array_view<gdb_byte> inst, int pos)
+{
+ if (pos < 0 || (inst.size () * 8) <= pos)
+ internal_error (_("bad bit offset: %d"), pos);
+
+ const int idx = pos >> 3;
+ const int off = pos & 7;
+ const int mask = 1 << off;
+ const gdb_byte byte = inst[idx];
+
+ const bool old = (byte & mask) != 0;
+ inst[idx] &= ~mask;
+
+ return old;
+}
+
+/* See arch/intelgt.h. */
+
+xe_version
+get_xe_version (uint32_t device_id)
+{
+ switch (device_id)
+ {
+ case 0x4F80:
+ case 0x4F81:
+ case 0x4F82:
+ case 0x4F83:
+ case 0x4F84:
+ case 0x4F85:
+ case 0x4F86:
+ case 0x4F87:
+ case 0x4F88:
+ case 0x5690:
+ case 0x5691:
+ case 0x5692:
+ case 0x5693:
+ case 0x5694:
+ case 0x5695:
+ case 0x5696:
+ case 0x5697:
+ case 0x5698:
+ case 0x56A0:
+ case 0x56A1:
+ case 0x56A2:
+ case 0x56A3:
+ case 0x56A4:
+ case 0x56A5:
+ case 0x56A6:
+ case 0x56A7:
+ case 0x56A8:
+ case 0x56A9:
+ case 0x56B0:
+ case 0x56B1:
+ case 0x56B2:
+ case 0x56B3:
+ case 0x56BA:
+ case 0x56BB:
+ case 0x56BC:
+ case 0x56BD:
+ case 0x56C0:
+ case 0x56C1:
+ case 0x56C2:
+ case 0x56CF:
+ case 0x7D40:
+ case 0x7D45:
+ case 0x7D67:
+ case 0x7D41:
+ case 0x7D55:
+ case 0x7DD5:
+ case 0x7D51:
+ case 0x7DD1:
+ return XE_HPG;
+
+ case 0x0201:
+ case 0x0202:
+ case 0x0203:
+ case 0x0204:
+ case 0x0205:
+ case 0x0206:
+ case 0x0207:
+ case 0x0208:
+ case 0x0209:
+ case 0x020A:
+ case 0x020B:
+ case 0x020C:
+ case 0x020D:
+ case 0x020E:
+ case 0x020F:
+ case 0x0210:
+ return XE_HP;
+
+ case 0x0BD0:
+ case 0x0BD4:
+ case 0x0BD5:
+ case 0x0BD6:
+ case 0x0BD7:
+ case 0x0BD8:
+ case 0x0BD9:
+ case 0x0BDA:
+ case 0x0BDB:
+ case 0x0B69:
+ case 0x0B6E:
+ return XE_HPC;
+
+ case 0x6420:
+ case 0x64A0:
+ case 0x64B0:
+
+ case 0xE202:
+ case 0xE20B:
+ case 0xE20C:
+ case 0xE20D:
+ case 0xE212:
+ return XE2;
+
+ case 0xB080:
+ case 0xB081:
+ case 0xB082:
+ case 0xB083:
+ case 0xB08F:
+ case 0xB090:
+ case 0xB0A0:
+ case 0xB0B0:
+ return XE3;
+
+ default:
+ return XE_INVALID;
+ }
+}
+
+} /* namespace intelgt */
diff --git a/gdb/arch/intelgt.h b/gdb/arch/intelgt.h
new file mode 100644
index 0000000000000000000000000000000000000000..2590887a30e74f978ac941accfb91b50523855a9
--- /dev/null
+++ b/gdb/arch/intelgt.h
@@ -0,0 +1,186 @@
+/* Copyright (C) 2019-2025 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
+};
+
+/* 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 XE 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),
+ XE3 = XE_VERSION (3, 0),
+};
+
+/* Helper function to translate the device id to a device version. */
+
+extern xe_version get_xe_version (uint32_t device_id);
+
+/* Get the bit at POS in INST. */
+
+bool get_inst_bit (gdb::array_view<const gdb_byte> inst, int pos);
+
+/* Set the bit at POS in INST. */
+
+bool set_inst_bit (gdb::array_view<gdb_byte> inst, int pos);
+
+/* Clear the bit at POS in INST. */
+
+bool clear_inst_bit (gdb::array_view<gdb_byte> inst, int pos);
+
+static inline int
+breakpoint_bit_offset (gdb::array_view<const gdb_byte> inst,
+ uint32_t device_id)
+{
+ xe_version device_version = get_xe_version (device_id);
+ switch (device_version)
+ {
+ case intelgt::XE_HP:
+ case intelgt::XE_HPG:
+ case intelgt::XE_HPC:
+ case intelgt::XE2:
+ case intelgt::XE3:
+ /* Check the CmptCtrl flag (bit 29). */
+ return (((inst[3] & 0x20) != 0) ? 7 : 30);
+
+ case intelgt::XE_INVALID:
+ break;
+ }
+ error (_("Unsupported device id 0x%" PRIx32), device_id);
+}
+
+static inline bool
+set_breakpoint (gdb::array_view<gdb_byte> inst, uint32_t device_id)
+{
+ return set_inst_bit (inst, breakpoint_bit_offset (inst, device_id));
+}
+
+static inline bool
+clear_breakpoint (gdb::array_view<gdb_byte> inst, uint32_t device_id)
+{
+ return clear_inst_bit (inst, breakpoint_bit_offset (inst, device_id));
+}
+
+static inline bool
+has_breakpoint (gdb::array_view<const gdb_byte> inst, uint32_t device_id)
+{
+ return get_inst_bit (inst, breakpoint_bit_offset (inst, device_id));
+}
+
+static inline unsigned int
+inst_length (gdb::array_view<const gdb_byte> inst, uint32_t device_id)
+{
+ xe_version device_version = get_xe_version (device_id);
+ switch (device_version)
+ {
+ case intelgt::XE_HP:
+ case intelgt::XE_HPG:
+ case intelgt::XE_HPC:
+ case intelgt::XE2:
+ case intelgt::XE3:
+ /* Check the CmptCtrl flag (bit 29). */
+ return (((inst[3] & 0x20) != 0)
+ ? COMPACT_INST_LENGTH
+ : MAX_INST_LENGTH);
+
+ case intelgt::XE_INVALID:
+ break;
+ }
+ error (_("Unsupported device id 0x%" PRIx32), device_id);
+}
+
+} /* 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] 92+ messages in thread* Re: [PATCH v3 06/44] gdb, arch, intelgt: add intelgt arch definitions
2025-08-01 9:37 ` [PATCH v3 06/44] gdb, arch, intelgt: add intelgt arch definitions Tankut Baris Aktemur
@ 2025-12-09 21:48 ` Simon Marchi
2025-12-16 15:47 ` Metzger, Markus T
0 siblings, 1 reply; 92+ messages in thread
From: Simon Marchi @ 2025-12-09 21:48 UTC (permalink / raw)
To: Tankut Baris Aktemur, gdb-patches, Markus Metzger
On 8/1/25 5:37 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>
> Reviewed-By: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
This LGTM, see nits below.
Approved-By: Simon Marchi <simon.marchi@efficios.com>
> +#include "gdbsupport/tdesc.h"
> +#include <string>
> +#include <vector>
clangd tells me that all these includes are unused.
> +
> +namespace intelgt {
Good idea to use a namespace here, we don't do that enough in GDB.
> +
> +/* Various arch constants. */
> +
> +enum breakpoint_kind
> +{
> + BP_INSTRUCTION = 1,
> +};
enum class, for new stuff? You can get rid of the prefix then.
> +
> +/* The length of a full and compact IntelGT instruction in bytes. */
> +
> +constexpr int MAX_INST_LENGTH = 16;
> +constexpr int COMPACT_INST_LENGTH = 8;
> +
> +/* Feature names.
> +
> + They correspond to register sets defined in zet_intel_gpu_debug.h. We
> + declare feature names in the order used in that header.
> +
> + The SBA register set consists of a set of base registers in the order
> + defined in that header file.
> +
> + Not all registers have DWARF numbers. See DWARF_REGSETS below for a
> + list of features that do. */
> +constexpr const char *FEATURE_GRF = "org.gnu.gdb.intelgt.grf";
> +constexpr const char *FEATURE_ADDR = "org.gnu.gdb.intelgt.addr";
> +constexpr const char *FEATURE_FLAG = "org.gnu.gdb.intelgt.flag";
> +constexpr const char *FEATURE_CE = "org.gnu.gdb.intelgt.ce";
> +constexpr const char *FEATURE_SR = "org.gnu.gdb.intelgt.sr";
> +constexpr const char *FEATURE_CR = "org.gnu.gdb.intelgt.cr";
> +constexpr const char *FEATURE_TDR = "org.gnu.gdb.intelgt.tdr";
> +constexpr const char *FEATURE_ACC = "org.gnu.gdb.intelgt.acc";
> +constexpr const char *FEATURE_MME = "org.gnu.gdb.intelgt.mme";
> +constexpr const char *FEATURE_SP = "org.gnu.gdb.intelgt.sp";
> +constexpr const char *FEATURE_SBA = "org.gnu.gdb.intelgt.sba";
> +constexpr const char *FEATURE_DBG = "org.gnu.gdb.intelgt.dbg";
> +constexpr const char *FEATURE_FC = "org.gnu.gdb.intelgt.fc";
> +constexpr const char *FEATURE_DEBUGGER = "org.gnu.gdb.intelgt.debugger";
> +
> +/* Register sets/groups needed for DWARF mapping. Used for
> + declaring static arrays for various mapping tables. */
> +
> +enum dwarf_regsets : int
> +{
> + REGSET_SBA = 0,
> + REGSET_GRF,
> + REGSET_ADDR,
> + REGSET_FLAG,
> + REGSET_ACC,
> + REGSET_MME,
> + REGSET_COUNT
> +};
> +
> +/* Map of dwarf_regset values to the target description
> + feature names. */
> +
> +constexpr const char *DWARF_REGSET_FEATURES[REGSET_COUNT] = {
> + FEATURE_SBA,
> + FEATURE_GRF,
> + FEATURE_ADDR,
> + FEATURE_FLAG,
> + FEATURE_ACC,
> + FEATURE_MME
> +};
> +
> +/* The encoding for XE version enumerates follows this pattern, which is
enumerates -> enumerators?
> + aligned with the IGA encoding. */
What is IGA? Around here it's a grocery store (https://www.iga.net). :)
> +
> +#define XE_VERSION(MAJ, MIN) (((MAJ) << 24) | (MIN))
With my modern C++ hat on: I am wondering if this can be a (constexpr?)
function, just because.
> +/* Supported GDB XE 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),
> + XE3 = XE_VERSION (3, 0),
> +};
> +
> +/* Helper function to translate the device id to a device version. */
> +
> +extern xe_version get_xe_version (uint32_t device_id);
I would remove this "extern" (and all the unnecessary uses of "extern"
in the project actually).
Simon
^ permalink raw reply [flat|nested] 92+ messages in thread* RE: [PATCH v3 06/44] gdb, arch, intelgt: add intelgt arch definitions
2025-12-09 21:48 ` Simon Marchi
@ 2025-12-16 15:47 ` Metzger, Markus T
0 siblings, 0 replies; 92+ messages in thread
From: Metzger, Markus T @ 2025-12-16 15:47 UTC (permalink / raw)
To: Simon Marchi; +Cc: Aktemur, Tankut Baris, gdb-patches
Hello Simon,
Thanks for your review.
>> +/* Various arch constants. */
>> +
>> +enum breakpoint_kind
>> +{
>> + BP_INSTRUCTION = 1,
>> +};
>
>enum class, for new stuff? You can get rid of the prefix then.
The only use is in breakpoint_kind_from_pc(), where it needs to decay
to int.
Similarly, the dwarf_regset enum below is used as array indices.
The only candidate for an enum class is xe_version.
>> +
>> +/* The length of a full and compact IntelGT instruction in bytes. */
>> +
>> +constexpr int MAX_INST_LENGTH = 16;
>> +constexpr int COMPACT_INST_LENGTH = 8;
>> +
>> +/* Feature names.
>> +
>> + They correspond to register sets defined in zet_intel_gpu_debug.h. We
>> + declare feature names in the order used in that header.
>> +
>> + The SBA register set consists of a set of base registers in the order
>> + defined in that header file.
>> +
>> + Not all registers have DWARF numbers. See DWARF_REGSETS below for a
>> + list of features that do. */
>> +constexpr const char *FEATURE_GRF = "org.gnu.gdb.intelgt.grf";
>> +constexpr const char *FEATURE_ADDR = "org.gnu.gdb.intelgt.addr";
>> +constexpr const char *FEATURE_FLAG = "org.gnu.gdb.intelgt.flag";
>> +constexpr const char *FEATURE_CE = "org.gnu.gdb.intelgt.ce";
>> +constexpr const char *FEATURE_SR = "org.gnu.gdb.intelgt.sr";
>> +constexpr const char *FEATURE_CR = "org.gnu.gdb.intelgt.cr";
>> +constexpr const char *FEATURE_TDR = "org.gnu.gdb.intelgt.tdr";
>> +constexpr const char *FEATURE_ACC = "org.gnu.gdb.intelgt.acc";
>> +constexpr const char *FEATURE_MME = "org.gnu.gdb.intelgt.mme";
>> +constexpr const char *FEATURE_SP = "org.gnu.gdb.intelgt.sp";
>> +constexpr const char *FEATURE_SBA = "org.gnu.gdb.intelgt.sba";
>> +constexpr const char *FEATURE_DBG = "org.gnu.gdb.intelgt.dbg";
>> +constexpr const char *FEATURE_FC = "org.gnu.gdb.intelgt.fc";
>> +constexpr const char *FEATURE_DEBUGGER =
>"org.gnu.gdb.intelgt.debugger";
>> +
>> +/* Register sets/groups needed for DWARF mapping. Used for
>> + declaring static arrays for various mapping tables. */
>> +
>> +enum dwarf_regsets : int
>> +{
>> + REGSET_SBA = 0,
>> + REGSET_GRF,
>> + REGSET_ADDR,
>> + REGSET_FLAG,
>> + REGSET_ACC,
>> + REGSET_MME,
>> + REGSET_COUNT
>> +};
>> +
>> +/* Map of dwarf_regset values to the target description
>> + feature names. */
>> +
>> +constexpr const char *DWARF_REGSET_FEATURES[REGSET_COUNT] = {
>> + FEATURE_SBA,
>> + FEATURE_GRF,
>> + FEATURE_ADDR,
>> + FEATURE_FLAG,
>> + FEATURE_ACC,
>> + FEATURE_MME
>> +};
>> +
>> +/* The encoding for XE version enumerates follows this pattern, which is
>
>enumerates -> enumerators?
>
>> + aligned with the IGA encoding. */
>
>What is IGA? Around here it's a grocery store (https://www.iga.net). :)
It's Intel Graphics (dis)Assembler. I spelled it out in the comment.
Regards,
Markus.
Intel Deutschland GmbH
Registered Address: Dornacher Straße 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht München HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread
* [PATCH v3 07/44] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (5 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 06/44] gdb, arch, intelgt: add intelgt arch definitions Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-12-11 18:53 ` Simon Marchi
2025-08-01 9:37 ` [PATCH v3 08/44] gdb, intelgt: add disassemble feature " Tankut Baris Aktemur
` (39 subsequent siblings)
46 siblings, 1 reply; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
Introduce gdb/intelgt-tdep.c for the Intel GT target. The target is
defined in a future patch as a gdbserver low target implementation.
Unlike, for example, X86-64, IntelGT does not have a dedicated
breakpoint instruction. Instead, it has a breakpoint bit in each
instruction. Hence, any instruction can be used as a breakpoint
instruction.
It further supports ignoring breakpoints for stepping over or resuming
from a breakpoint. This only works, of course, if we use breakpoint
bits inside the original instruction rather than replacing it with a
fixed breakpoint instruction.
Add gdbarch methods for inserting and removing memory breakpoints by
setting and clearing those breakpoint bits.
We define one pseudo-register, $framedesc, that provides a type alias
for the frame descriptor register in GRF, representing it as a struct.
The value of the $pc register is the $ip register, which is provided
in $cr0.2, offset by $isabase.
A translation function to map the device_id to a gen_version is
provided. This will be useful in various places, in particular to
generate the correct disassembly.
Co-authored-by: Markus Metzger <markus.t.metzger@intel.com>
Co-authored-by: Natalia Saiapova <natalia.saiapova@intel.com>
Co-authored-by: Mihails Strasuns <mihails.strasuns@intel.com>
Co-authored-by: Mohamed Bouhaouel <mohamed.bouhaouel@intel.com>
---
gdb/Makefile.in | 1 +
gdb/configure.tgt | 5 +
gdb/intelgt-tdep.c | 875 ++++++++++++++++++++++++++++++++++++++++++++++++++++
gdb/selftest-arch.c | 6 +-
4 files changed, 886 insertions(+), 1 deletion(-)
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 8aac71d7057980a45e3cb89be2c583223762a1c7..a6f32b5c02877592ff3072a431b7e229e031ebde 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -762,6 +762,7 @@ ALL_64_TARGET_OBS = \
ia64-linux-tdep.o \
ia64-tdep.o \
ia64-vms-tdep.o \
+ intelgt-tdep.o \
loongarch-linux-tdep.o \
loongarch-tdep.o \
mips-fbsd-tdep.o \
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 255c77e9f8c0d642fa1773b54ae7dfaaf25e6ef5..aa0336d874e290dcb4efedfa4af432c1a15293c6 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -357,6 +357,11 @@ ia64-*-*vms*)
gdb_target_obs="ia64-vms-tdep.o"
;;
+intelgt-*-elf)
+ # Target: Intel(R) Graphics Technology graphics processor
+ gdb_target_obs="intelgt-tdep.o arch/intelgt.o"
+ ;;
+
iq2000-*-*)
gdb_target_obs="iq2000-tdep.o"
;;
diff --git a/gdb/intelgt-tdep.c b/gdb/intelgt-tdep.c
new file mode 100644
index 0000000000000000000000000000000000000000..3919947fed44c9e939bef6c14b854ae416ab7c41
--- /dev/null
+++ b/gdb/intelgt-tdep.c
@@ -0,0 +1,875 @@
+/* Target-dependent code for the Intel(R) Graphics Technology architecture.
+
+ Copyright (C) 2019-2025 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "arch-utils.h"
+#include "arch/intelgt.h"
+#include "cli/cli-cmds.h"
+#include "dwarf2/frame.h"
+#include "frame-unwind.h"
+#include "gdbsupport/gdb_obstack.h"
+#include "gdbtypes.h"
+#include "target.h"
+#include "target-descriptions.h"
+#include "value.h"
+#include "gdbthread.h"
+#include "inferior.h"
+#include "user-regs.h"
+#include <algorithm>
+
+/* Global debug flag. */
+static bool intelgt_debug = false;
+
+/* Print an "intelgt" debug statement. */
+
+#define intelgt_debug_printf(fmt, ...) \
+ debug_prefixed_printf_cond (intelgt_debug, "intelgt", fmt, ##__VA_ARGS__)
+
+/* Regnum pair describing the assigned regnum range for a single
+ regset. START is inclusive, END is exclusive. */
+
+struct regnum_range
+{
+ int start;
+ int end;
+};
+
+/* Data specific for this architecture. */
+
+struct intelgt_gdbarch_tdep : gdbarch_tdep_base
+{
+ /* $r0 GRF register number. */
+ int r0_regnum = -1;
+
+ /* $ce register number in the regcache. */
+ int ce_regnum = -1;
+
+ /* Register number for the GRF containing function return value. */
+ int retval_regnum = -1;
+
+ /* Register number for the control register. */
+ int cr0_regnum = -1;
+
+ /* Register number for the state register. */
+ int sr0_regnum = -1;
+
+ /* Register number for the instruction base virtual register. */
+ int isabase_regnum = -1;
+
+ /* Register number for the general state base SBA register. */
+ int genstbase_regnum = -1;
+
+ /* Register number for the DBG0 register. */
+ int dbg0_regnum = -1;
+
+ /* Assigned regnum ranges for DWARF regsets. */
+ regnum_range regset_ranges[intelgt::REGSET_COUNT];
+
+ /* Enabled pseudo-registers for the current target description. */
+ std::vector<std::string> enabled_pseudo_regs;
+
+ /* Cached $framedesc pseudo-register type. */
+ type *framedesc_type = nullptr;
+
+ /* Initialize ranges to -1 as "not-yet-set" indicator. */
+ intelgt_gdbarch_tdep ()
+ {
+ memset (®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;
+ }
+};
+
+/* Per-inferior cached data for the Intelgt target. */
+
+struct intelgt_inferior_data
+{
+ /* Device target id. */
+ uint32_t device_id = 0u;
+};
+
+static const registry<inferior>::key<intelgt_inferior_data>
+ intelgt_inferior_data_handle;
+
+/* Fetch the per-inferior data. */
+
+static intelgt_inferior_data *
+get_intelgt_inferior_data (inferior *inf)
+{
+ intelgt_inferior_data *inf_data = intelgt_inferior_data_handle.get (inf);
+ if (inf_data == nullptr)
+ inf_data = intelgt_inferior_data_handle.emplace (inf);
+
+ return inf_data;
+}
+
+/* Helper function to return the device id using GDBARCH. */
+
+static uint32_t
+get_device_id (gdbarch *gdbarch)
+{
+ const target_desc *tdesc = gdbarch_target_desc (gdbarch);
+ const tdesc_device *device_info = tdesc_device_info (tdesc);
+ if (!device_info->target_id.has_value ())
+ error (_("A target id for the device is required."));
+
+ return *device_info->target_id;
+}
+
+/* Helper function to return the device id using the inferior. */
+
+static uint32_t
+get_device_id (inferior *inferior)
+{
+ intelgt_inferior_data *inf_data = get_intelgt_inferior_data (inferior);
+ if (inf_data->device_id == 0u)
+ inf_data->device_id = get_device_id (inferior->arch ());
+
+ return inf_data->device_id;
+}
+
+/* Read part of REGNUM at OFFSET into BUFFER. The length of data to
+ read is SIZE. Consider using this helper function when reading
+ subregisters of CR0, SR0, and R0. */
+
+static void
+intelgt_read_register_part (readable_regcache *regcache, int regnum,
+ size_t offset,
+ gdb::array_view<gdb_byte> buffer,
+ const char *error_message)
+{
+ if (regnum == -1)
+ error (_("%s Unexpected reg num '-1'."), error_message);
+
+ gdbarch *arch = regcache->arch ();
+ const char *regname = gdbarch_register_name (arch, regnum);
+ int regsize = register_size (arch, regnum);
+
+ if (offset + buffer.size () > regsize)
+ error (_("%s %s[%zu:%zu] is outside the range of %s[%d:0]."),
+ error_message, regname, (offset + buffer.size () - 1), offset,
+ regname, (regsize - 1));
+
+ register_status reg_status
+ = regcache->cooked_read_part (regnum, offset, buffer);
+
+ if (reg_status == REG_UNAVAILABLE)
+ throw_error (NOT_AVAILABLE_ERROR,
+ _("%s Register %s (%d) is not available."),
+ error_message, regname, regnum);
+
+ if (reg_status == REG_UNKNOWN)
+ error (_("%s Register %s (%d) is unknown."), error_message,
+ regname, regnum);
+}
+
+/* Utility function to look up the pseudo-register number by name. Exact
+ amount of pseudo-registers may differ and thus fixed constants can't be
+ used for this. */
+
+static int
+intelgt_pseudo_register_num (gdbarch *arch, const char *name)
+{
+ intelgt_gdbarch_tdep *data = gdbarch_tdep<intelgt_gdbarch_tdep> (arch);
+ auto iter = std::find (data->enabled_pseudo_regs.begin (),
+ data->enabled_pseudo_regs.end (), name);
+ gdb_assert (iter != data->enabled_pseudo_regs.end ());
+ return gdbarch_num_regs (arch) + (iter - data->enabled_pseudo_regs.begin ());
+}
+
+/* Convert a DWARF register number to a GDB register number. This
+ function requires the register listing in the target description to
+ be in the same order in each regset as the intended DWARF numbering
+ order. Currently this always holds true when gdbserver generates
+ the target description. */
+
+static int
+intelgt_dwarf_reg_to_regnum (gdbarch *gdbarch, int num)
+{
+ constexpr int ip = 0;
+ constexpr int ce = 1;
+
+ /* Register sets follow this format: [START, END), where START is
+ inclusive and END is exclusive. */
+ constexpr regnum_range dwarf_nums[intelgt::REGSET_COUNT] = {
+ [intelgt::REGSET_SBA] = { 5, 12 },
+ [intelgt::REGSET_GRF] = { 16, 272 },
+ [intelgt::REGSET_ADDR] = { 272, 288 },
+ [intelgt::REGSET_FLAG] = { 288, 304 },
+ [intelgt::REGSET_ACC] = { 304, 320 },
+ [intelgt::REGSET_MME] = { 320, 336 },
+ };
+
+ /* Number of SBA registers. */
+ constexpr size_t sba_dwarf_len = dwarf_nums[intelgt::REGSET_SBA].end
+ - dwarf_nums[intelgt::REGSET_SBA].start;
+
+ /* Map the DWARF register numbers of SBA registers to their names.
+ Base number is dwarf_nums[intelgt::REGSET_SBA].start. */
+ constexpr const char* sba_dwarf_reg_order[sba_dwarf_len] {
+ "btbase",
+ "scrbase",
+ "genstbase",
+ "sustbase",
+ "blsustbase",
+ "blsastbase",
+ "scrbase2"
+ };
+
+ intelgt_gdbarch_tdep *data
+ = gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch);
+
+ if (num == ip)
+ return intelgt_pseudo_register_num (gdbarch, "ip");
+ if (num == ce)
+ return data->ce_regnum;
+
+ for (int regset = 0; regset < intelgt::REGSET_COUNT; ++regset)
+ if (num >= dwarf_nums[regset].start && num < dwarf_nums[regset].end)
+ {
+ if (regset == intelgt::REGSET_SBA)
+ {
+ /* For SBA registers we first find out the name of the
+ register out of DWARF register number and then find the
+ register number corresponding to the name. */
+ int sba_num = num - dwarf_nums[intelgt::REGSET_SBA].start;
+ const char* name = sba_dwarf_reg_order [sba_num];
+
+ return user_reg_map_name_to_regnum (gdbarch, name, -1);
+ }
+ else
+ {
+ int candidate = data->regset_ranges[regset].start + num
+ - dwarf_nums[regset].start;
+
+ if (candidate < data->regset_ranges[regset].end)
+ return candidate;
+ }
+ }
+
+ return -1;
+}
+
+/* Return the PC of the first real instruction. */
+
+static CORE_ADDR
+intelgt_skip_prologue (gdbarch *gdbarch, CORE_ADDR start_pc)
+{
+ intelgt_debug_printf ("start_pc: %s", paddress (gdbarch, start_pc));
+ CORE_ADDR func_addr;
+
+ if (find_pc_partial_function (start_pc, nullptr, &func_addr, nullptr))
+ {
+ CORE_ADDR post_prologue_pc
+ = skip_prologue_using_sal (gdbarch, func_addr);
+
+ intelgt_debug_printf ("post prologue pc: %s",
+ paddress (gdbarch, post_prologue_pc));
+
+ if (post_prologue_pc != 0)
+ return post_prologue_pc;
+ }
+
+ /* Could not find the end of prologue using SAL. */
+ return start_pc;
+}
+
+/* Implementation of gdbarch's return_value method. */
+
+static enum return_value_convention
+intelgt_return_value_as_value (gdbarch *gdbarch, value *function,
+ type *valtype, regcache *regcache,
+ value **read_value, const gdb_byte *writebuf)
+{
+ gdb_assert_not_reached ("intelgt_return_value_as_value is to be "
+ "implemented later.");
+}
+
+/* Callback function to unwind the $framedesc register. */
+
+static value *
+intelgt_dwarf2_prev_framedesc (const frame_info_ptr &this_frame,
+ void **this_cache, int regnum)
+{
+ gdbarch *gdbarch = get_frame_arch (this_frame);
+ intelgt_gdbarch_tdep *data
+ = gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch);
+
+ int actual_regnum = data->framedesc_base_regnum ();
+
+ /* Unwind the actual GRF register. */
+ return frame_unwind_register_value (this_frame, actual_regnum);
+}
+
+/* Architecture-specific register state initialization. */
+
+static void
+intelgt_init_reg (gdbarch *gdbarch, int regnum, dwarf2_frame_state_reg *reg,
+ const frame_info_ptr &this_frame)
+{
+ int ip_regnum = intelgt_pseudo_register_num (gdbarch, "ip");
+ int framedesc_regnum = intelgt_pseudo_register_num (gdbarch, "framedesc");
+
+ if (regnum == ip_regnum)
+ reg->how = DWARF2_FRAME_REG_RA;
+ else if (regnum == gdbarch_sp_regnum (gdbarch))
+ reg->how = DWARF2_FRAME_REG_CFA;
+ /* We use a special function to unwind the $framedesc register. */
+ else if (regnum == framedesc_regnum)
+ {
+ reg->how = DWARF2_FRAME_REG_FN;
+ reg->loc.fn = intelgt_dwarf2_prev_framedesc;
+ }
+}
+
+/* A helper function that returns the value of the ISABASE register. */
+
+static CORE_ADDR
+intelgt_get_isabase (readable_regcache *regcache)
+{
+ gdbarch *gdbarch = regcache->arch ();
+ intelgt_gdbarch_tdep *data
+ = gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch);
+ gdb_assert (data->isabase_regnum != -1);
+
+ uint64_t isabase = 0;
+ if (regcache->cooked_read (data->isabase_regnum, &isabase) != REG_VALID)
+ throw_error (NOT_AVAILABLE_ERROR,
+ _("Register %d (isabase) is not available"),
+ data->isabase_regnum);
+ return isabase;
+}
+
+/* The 'unwind_pc' gdbarch method. */
+
+static CORE_ADDR
+intelgt_unwind_pc (gdbarch *gdbarch, const frame_info_ptr &next_frame)
+{
+ /* Use ip register here, as IGC uses 32bit values (pc is 64bit). */
+ int ip_regnum = intelgt_pseudo_register_num (gdbarch, "ip");
+ CORE_ADDR prev_ip = frame_unwind_register_unsigned (next_frame,
+ ip_regnum);
+ intelgt_debug_printf ("prev_ip: %s", paddress (gdbarch, prev_ip));
+
+ /* Program counter is $ip + $isabase. Read directly from the
+ regcache instead of unwinding, as the frame unwind info may
+ simply be unavailable. The isabase register does not change
+ during kernel execution, so this must be safe. */
+ regcache *regcache = get_thread_regcache (inferior_thread ());
+ CORE_ADDR isabase = intelgt_get_isabase (regcache);
+
+ return isabase + prev_ip;
+}
+
+/* Frame unwinding. */
+
+static void
+intelgt_frame_this_id (const frame_info_ptr &this_frame,
+ void **this_prologue_cache, frame_id *this_id)
+{
+ /* FIXME: Assembly-level unwinding for intelgt is not available at
+ the moment. Stop at the first frame. */
+ *this_id = outer_frame_id;
+}
+
+/* This is a "bare minimum" unwinder that avoids crashing GDB
+ if debugging a program without debug info. */
+
+static const frame_unwind_legacy intelgt_frame_unwind (
+ "intelgt prologue",
+ NORMAL_FRAME, /* type */
+ FRAME_UNWIND_ARCH, /* class */
+ default_frame_unwind_stop_reason, /* stop_reason */
+ intelgt_frame_this_id, /* this_id */
+ nullptr, /* prev_register */
+ nullptr, /* unwind_data */
+ default_frame_sniffer /* sniffer */
+);
+
+/* The memory_insert_breakpoint gdbarch method. */
+
+static int
+intelgt_memory_insert_breakpoint (gdbarch *gdbarch, bp_target_info *bp)
+{
+ intelgt_debug_printf ("req ip: %s", paddress (gdbarch,
+ bp->reqstd_address));
+
+ /* Ensure that we have enough space in the breakpoint. */
+ static_assert (intelgt::MAX_INST_LENGTH <= BREAKPOINT_MAX);
+
+ gdb_byte inst[intelgt::MAX_INST_LENGTH];
+ int err = target_read_memory (bp->reqstd_address, inst,
+ intelgt::MAX_INST_LENGTH);
+ if (err != 0)
+ {
+ /* We could fall back to reading a full and then a compacted
+ instruction but I think we should rather allow short reads than
+ having the caller try smaller and smaller sizes. */
+ intelgt_debug_printf ("Failed to read memory at %s (%s).",
+ paddress (gdbarch, bp->reqstd_address),
+ strerror (err));
+ return err;
+ }
+
+ bp->placed_address = bp->reqstd_address;
+ uint32_t device_id = get_device_id (current_inferior ());
+ bp->shadow_len = intelgt::inst_length (inst, device_id);
+
+ /* Make a copy before we set the breakpoint so we can restore the
+ original instruction when removing the breakpoint again.
+
+ This isn't strictly necessary but it saves one target access. */
+ memcpy (bp->shadow_contents, inst, bp->shadow_len);
+
+ const bool already = intelgt::set_breakpoint (inst, device_id);
+ if (already)
+ {
+ /* Warn if the breakpoint bit is already set.
+
+ There is still a breakpoint, probably hard-coded, and it should
+ still trigger and we're still able to step over it. It's just
+ not our breakpoint. */
+ warning (_("Using permanent breakpoint at %s."),
+ paddress (gdbarch, bp->placed_address));
+
+ /* There's no need to write the unmodified instruction back. */
+ return 0;
+ }
+
+ err = target_write_raw_memory (bp->placed_address, inst, bp->shadow_len);
+ if (err != 0)
+ intelgt_debug_printf ("Failed to insert breakpoint at %s (%s).",
+ paddress (gdbarch, bp->placed_address),
+ strerror (err));
+
+ return err;
+}
+
+/* The memory_remove_breakpoint gdbarch method. */
+
+static int
+intelgt_memory_remove_breakpoint (gdbarch *gdbarch, struct bp_target_info *bp)
+{
+ intelgt_debug_printf ("req ip: %s, placed ip: %s",
+ paddress (gdbarch, bp->reqstd_address),
+ paddress (gdbarch, bp->placed_address));
+
+ /* Warn if we're inserting a permanent breakpoint. */
+ uint32_t device_id = get_device_id (current_inferior ());
+ if (intelgt::has_breakpoint (bp->shadow_contents, device_id))
+ warning (_("Re-inserting permanent breakpoint at %s."),
+ paddress (gdbarch, bp->placed_address));
+
+ int err = target_write_raw_memory (bp->placed_address, bp->shadow_contents,
+ bp->shadow_len);
+ if (err != 0)
+ intelgt_debug_printf ("Failed to remove breakpoint at %s (%s).",
+ paddress (gdbarch, bp->placed_address),
+ strerror (err));
+
+ return err;
+}
+
+/* The program_breakpoint_here_p gdbarch method. */
+
+static bool
+intelgt_program_breakpoint_here_p (gdbarch *gdbarch, CORE_ADDR pc)
+{
+ intelgt_debug_printf ("pc: %s", paddress (gdbarch, pc));
+
+ gdb_byte inst[intelgt::MAX_INST_LENGTH];
+ int err = target_read_memory (pc, inst, intelgt::MAX_INST_LENGTH);
+ if (err != 0)
+ {
+ /* We could fall back to reading a full and then a compacted
+ instruction but I think we should rather allow short reads than
+ having the caller try smaller and smaller sizes. */
+ intelgt_debug_printf ("Failed to read memory at %s (%s).",
+ paddress (gdbarch, pc), strerror (err));
+ return err;
+ }
+
+ uint32_t device_id = get_device_id (current_inferior ());
+ const bool is_bkpt = intelgt::has_breakpoint (inst, device_id);
+
+ intelgt_debug_printf ("%sbreakpoint found.", is_bkpt ? "" : "no ");
+
+ return is_bkpt;
+}
+
+/* The 'breakpoint_kind_from_pc' gdbarch method.
+ This is a required gdbarch function. */
+
+static int
+intelgt_breakpoint_kind_from_pc (gdbarch *gdbarch, CORE_ADDR *pcptr)
+{
+ intelgt_debug_printf ("*pcptr: %s", paddress (gdbarch, *pcptr));
+
+ return intelgt::BP_INSTRUCTION;
+}
+
+/* The 'sw_breakpoint_from_kind' gdbarch method. */
+
+static const gdb_byte *
+intelgt_sw_breakpoint_from_kind (gdbarch *gdbarch, int kind, int *size)
+{
+ intelgt_debug_printf ("kind: %d", kind);
+
+ /* We do not support breakpoint instructions.
+
+ We use breakpoint bits in instructions, instead. See
+ intelgt_memory_insert_breakpoint. */
+ *size = 0;
+ return nullptr;
+}
+
+/* Print one instruction from MEMADDR on INFO->STREAM. */
+
+static int
+intelgt_print_insn (bfd_vma memaddr, struct disassemble_info *info)
+{
+ /* Disassembler is to be added in a later patch. */
+ return -1;
+}
+
+/* The "read_pc" gdbarch method. */
+
+static CORE_ADDR
+intelgt_read_pc (readable_regcache *regcache)
+{
+ gdbarch *arch = regcache->arch ();
+ intelgt_gdbarch_tdep *data = gdbarch_tdep<intelgt_gdbarch_tdep> (arch);
+
+ /* Instruction pointer is stored in CR0.2. */
+ uint32_t ip;
+ intelgt_read_register_part (regcache, data->cr0_regnum,
+ sizeof (uint32_t) * 2,
+ gdb::make_array_view ((gdb_byte *) &ip,
+ sizeof (uint32_t)),
+ _("Cannot compute PC."));
+
+ /* Program counter is $ip + $isabase. */
+ CORE_ADDR isabase = intelgt_get_isabase (regcache);
+ return isabase + ip;
+}
+
+/* The "write_pc" gdbarch method. */
+
+static void
+intelgt_write_pc (struct regcache *regcache, CORE_ADDR pc)
+{
+ gdbarch *arch = regcache->arch ();
+ /* Program counter is $ip + $isabase, can only modify $ip. Need
+ to ensure that the new value fits within $ip modification range
+ and propagate the write accordingly. */
+ CORE_ADDR isabase = intelgt_get_isabase (regcache);
+ if (pc < isabase || pc > isabase + UINT32_MAX)
+ error (_("Can't update $pc to value %s, out of range"),
+ paddress (arch, pc));
+
+ intelgt_gdbarch_tdep *data = gdbarch_tdep<intelgt_gdbarch_tdep> (arch);
+
+ /* Instruction pointer is stored in CR0.2. */
+ uint32_t ip = pc - isabase;
+ regcache->cooked_write_part (data->cr0_regnum, sizeof (uint32_t) * 2,
+ gdb::make_array_view ((gdb_byte *) &ip,
+ sizeof (uint32_t)));
+}
+
+/* Return the name of pseudo-register REGNUM. */
+
+static const char *
+intelgt_pseudo_register_name (gdbarch *arch, int regnum)
+{
+ intelgt_gdbarch_tdep *data = gdbarch_tdep<intelgt_gdbarch_tdep> (arch);
+ int base_num = gdbarch_num_regs (arch);
+ if (regnum < base_num
+ || regnum >= base_num + data->enabled_pseudo_regs.size ())
+ error (_("Invalid pseudo-register regnum %d"), regnum);
+ return data->enabled_pseudo_regs[regnum - base_num].c_str ();
+}
+
+/* Return the GDB type object for the "standard" data type of data in
+ pseudo-register REGNUM. */
+
+static type *
+intelgt_pseudo_register_type (gdbarch *arch, int regnum)
+{
+ const char *name = intelgt_pseudo_register_name (arch, regnum);
+ const struct builtin_type *bt = builtin_type (arch);
+ intelgt_gdbarch_tdep *data = gdbarch_tdep<intelgt_gdbarch_tdep> (arch);
+
+ if (strcmp (name, "framedesc") == 0)
+ {
+ if (data->framedesc_type != nullptr)
+ return data->framedesc_type;
+ type *frame = arch_composite_type (arch, "frame_desc", TYPE_CODE_STRUCT);
+ append_composite_type_field (frame, "return_ip", bt->builtin_uint32);
+ append_composite_type_field (frame, "return_callmask",
+ bt->builtin_uint32);
+ append_composite_type_field (frame, "be_sp", bt->builtin_uint32);
+ append_composite_type_field (frame, "be_fp", bt->builtin_uint32);
+ append_composite_type_field (frame, "fe_fp", bt->builtin_data_ptr);
+ append_composite_type_field (frame, "fe_sp", bt->builtin_data_ptr);
+ data->framedesc_type = frame;
+ return frame;
+ }
+ else if (strcmp (name, "ip") == 0)
+ return bt->builtin_uint32;
+
+ return nullptr;
+}
+
+/* Read the value of a pseudo-register REGNUM. */
+
+static struct value *
+intelgt_pseudo_register_read_value (gdbarch *arch,
+ const frame_info_ptr &next_frame,
+ int pseudo_regnum)
+{
+ const char *name = intelgt_pseudo_register_name (arch, pseudo_regnum);
+ intelgt_gdbarch_tdep *data = gdbarch_tdep<intelgt_gdbarch_tdep> (arch);
+
+ if (strcmp (name, "framedesc") == 0)
+ {
+ int grf_num = data->framedesc_base_regnum ();
+ return pseudo_from_raw_part (next_frame, pseudo_regnum, grf_num, 0);
+ }
+ else if (strcmp (name, "ip") == 0)
+ {
+ int regsize = register_size (arch, pseudo_regnum);
+ /* Instruction pointer is stored in CR0.2. */
+ gdb_assert (data->cr0_regnum != -1);
+ /* CR0 elements are 4 byte wide. */
+ gdb_assert (regsize + 8 <= register_size (arch, data->cr0_regnum));
+
+ return pseudo_from_raw_part (next_frame, pseudo_regnum,
+ data->cr0_regnum, 8);
+ }
+
+ return nullptr;
+}
+
+/* Write the value of a pseudo-register REGNUM. */
+
+static void
+intelgt_pseudo_register_write (gdbarch *arch,
+ const frame_info_ptr &next_frame,
+ int pseudo_regnum,
+ gdb::array_view<const gdb_byte> buf)
+{
+ const char *name = intelgt_pseudo_register_name (arch, pseudo_regnum);
+ intelgt_gdbarch_tdep *data = gdbarch_tdep<intelgt_gdbarch_tdep> (arch);
+
+ if (strcmp (name, "framedesc") == 0)
+ {
+ int grf_num = data->framedesc_base_regnum ();
+ int grf_size = register_size (arch, grf_num);
+ int desc_size = register_size (arch, pseudo_regnum);
+ gdb_assert (grf_size >= desc_size);
+ pseudo_to_raw_part (next_frame, buf, grf_num, 0);
+ }
+ else if (strcmp (name, "ip") == 0)
+ {
+ /* Instruction pointer is stored in CR0.2. */
+ gdb_assert (data->cr0_regnum != -1);
+ int cr0_size = register_size (arch, data->cr0_regnum);
+
+ /* CR0 elements are 4 byte wide. */
+ int reg_size = register_size (arch, pseudo_regnum);
+ gdb_assert (reg_size + 8 <= cr0_size);
+ pseudo_to_raw_part (next_frame, buf, data->cr0_regnum, 8);
+ }
+ else
+ error ("Pseudo-register %s is read-only", name);
+}
+
+/* Called by tdesc_use_registers each time a new regnum
+ is assigned. Used to track down assigned numbers for
+ any important regnums. */
+
+static int
+intelgt_unknown_register_cb (gdbarch *arch, tdesc_feature *feature,
+ const char *reg_name, int possible_regnum)
+{
+ intelgt_gdbarch_tdep *data = gdbarch_tdep<intelgt_gdbarch_tdep> (arch);
+
+ /* First, check if this a beginning of a not yet tracked regset
+ assignment. */
+
+ for (int regset = 0; regset < intelgt::REGSET_COUNT; ++regset)
+ {
+ if (data->regset_ranges[regset].start == -1
+ && feature->name == intelgt::DWARF_REGSET_FEATURES[regset])
+ {
+ data->regset_ranges[regset].start = possible_regnum;
+ data->regset_ranges[regset].end
+ = feature->registers.size () + possible_regnum;
+ break;
+ }
+ }
+
+ /* Second, check if it is any specific individual register that
+ needs to be tracked. */
+
+ if (strcmp ("r0", reg_name) == 0)
+ data->r0_regnum = possible_regnum;
+ else if (strcmp ("r26", reg_name) == 0)
+ data->retval_regnum = possible_regnum;
+ else if (strcmp ("cr0", reg_name) == 0)
+ data->cr0_regnum = possible_regnum;
+ else if (strcmp ("sr0", reg_name) == 0)
+ data->sr0_regnum = possible_regnum;
+ else if (strcmp ("isabase", reg_name) == 0)
+ data->isabase_regnum = possible_regnum;
+ else if (strcmp ("ce", reg_name) == 0)
+ data->ce_regnum = possible_regnum;
+ else if (strcmp ("genstbase", reg_name) == 0)
+ data->genstbase_regnum = possible_regnum;
+ else if (strcmp ("dbg0", reg_name) == 0)
+ data->dbg0_regnum = possible_regnum;
+
+ return possible_regnum;
+}
+
+/* Architecture initialization. */
+
+static gdbarch *
+intelgt_gdbarch_init (gdbarch_info info, gdbarch_list *arches)
+{
+ /* If there is already a candidate, use it. */
+ arches = gdbarch_list_lookup_by_info (arches, &info);
+ if (arches != nullptr)
+ return arches->gdbarch;
+
+ const target_desc *tdesc = info.target_desc;
+ gdbarch_up gdbarch_u
+ (gdbarch_alloc (&info,
+ gdbarch_tdep_up (new intelgt_gdbarch_tdep)));
+ gdbarch *gdbarch = gdbarch_u.get ();
+ intelgt_gdbarch_tdep *data
+ = gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch);
+
+ /* 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, tdesc_register_type);
+ set_gdbarch_dwarf2_reg_to_regnum (gdbarch, intelgt_dwarf_reg_to_regnum);
+
+ set_gdbarch_skip_prologue (gdbarch, intelgt_skip_prologue);
+ set_gdbarch_inner_than (gdbarch, core_addr_greaterthan);
+ set_gdbarch_unwind_pc (gdbarch, intelgt_unwind_pc);
+ dwarf2_append_unwinders (gdbarch);
+ frame_unwind_append_unwinder (gdbarch, &intelgt_frame_unwind);
+
+ set_gdbarch_return_value_as_value (gdbarch, intelgt_return_value_as_value);
+
+ set_gdbarch_memory_insert_breakpoint (gdbarch,
+ intelgt_memory_insert_breakpoint);
+ set_gdbarch_memory_remove_breakpoint (gdbarch,
+ intelgt_memory_remove_breakpoint);
+ set_gdbarch_program_breakpoint_here_p (gdbarch,
+ intelgt_program_breakpoint_here_p);
+ set_gdbarch_breakpoint_kind_from_pc (gdbarch,
+ intelgt_breakpoint_kind_from_pc);
+ set_gdbarch_sw_breakpoint_from_kind (gdbarch,
+ intelgt_sw_breakpoint_from_kind);
+ dwarf2_frame_set_init_reg (gdbarch, intelgt_init_reg);
+
+ /* Disassembly. */
+ set_gdbarch_print_insn (gdbarch, intelgt_print_insn);
+
+ return gdbarch_u.release ();
+}
+
+static void
+show_intelgt_debug (ui_file *file, int from_tty,
+ cmd_list_element *c, const char *value)
+{
+ gdb_printf (file, _("Intel(R) Graphics Technology debugging is "
+ "%s.\n"), value);
+}
+
+INIT_GDB_FILE (intelgt_tdep)
+{
+ gdbarch_register (bfd_arch_intelgt, intelgt_gdbarch_init);
+
+ /* Debugging flag. */
+ add_setshow_boolean_cmd ("intelgt", class_maintenance, &intelgt_debug,
+ _("Set Intel(R) Graphics Technology debugging."),
+ _("Show Intel(R) Graphics Technology debugging."),
+ _("When on, Intel(R) Graphics Technology "
+ "debugging is enabled."),
+ nullptr,
+ show_intelgt_debug,
+ &setdebuglist, &showdebuglist);
+}
diff --git a/gdb/selftest-arch.c b/gdb/selftest-arch.c
index 452258c0eb9a1346222477ae682b712f45d00729..1cbb065e5811e16c746d2d0061321b2fbdf5b3ea 100644
--- a/gdb/selftest-arch.c
+++ b/gdb/selftest-arch.c
@@ -122,9 +122,13 @@ selftest_skip_warning_arch (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);
}
} /* namespace selftests */
--
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] 92+ messages in thread* Re: [PATCH v3 07/44] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture
2025-08-01 9:37 ` [PATCH v3 07/44] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture Tankut Baris Aktemur
@ 2025-12-11 18:53 ` Simon Marchi
2025-12-19 16:01 ` Aktemur, Tankut Baris
0 siblings, 1 reply; 92+ messages in thread
From: Simon Marchi @ 2025-12-11 18:53 UTC (permalink / raw)
To: Tankut Baris Aktemur, gdb-patches, Markus Metzger
On 2025-08-01 05:37, 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.
>
> Unlike, for example, X86-64, IntelGT does not have a dedicated
> breakpoint instruction. Instead, it has a breakpoint bit in each
> instruction. Hence, any instruction can be used as a breakpoint
> instruction.
>
> It further supports ignoring breakpoints for stepping over or resuming
> from a breakpoint. This only works, of course, if we use breakpoint
> bits inside the original instruction rather than replacing it with a
> fixed breakpoint instruction.
>
> Add gdbarch methods for inserting and removing memory breakpoints by
> setting and clearing those breakpoint bits.
>
> We define one pseudo-register, $framedesc, that provides a type alias
> for the frame descriptor register in GRF, representing it as a struct.
>
> The value of the $pc register is the $ip register, which is provided
> in $cr0.2, offset by $isabase.
>
> A translation function to map the device_id to a gen_version is
> provided. This will be useful in various places, in particular to
> generate the correct disassembly.
>
> Co-authored-by: Markus Metzger <markus.t.metzger@intel.com>
> Co-authored-by: Natalia Saiapova <natalia.saiapova@intel.com>
> Co-authored-by: Mihails Strasuns <mihails.strasuns@intel.com>
> Co-authored-by: Mohamed Bouhaouel <mohamed.bouhaouel@intel.com>
Only minor comments noted below.
Approved-By: Simon Marchi <simon.marchi@efficios.com>
> diff --git a/gdb/intelgt-tdep.c b/gdb/intelgt-tdep.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..3919947fed44c9e939bef6c14b854ae416ab7c41
> --- /dev/null
> +++ b/gdb/intelgt-tdep.c
> @@ -0,0 +1,875 @@
> +/* Target-dependent code for the Intel(R) Graphics Technology architecture.
> +
> + Copyright (C) 2019-2025 Free Software Foundation, Inc.
> +
> + This file is part of GDB.
> +
> + This program is free software; you can redistribute it and/or modify
> + it under the terms of the GNU General Public License as published by
> + the Free Software Foundation; either version 3 of the License, or
> + (at your option) any later version.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program. If not, see <http://www.gnu.org/licenses/>. */
> +
> +#include "arch-utils.h"
> +#include "arch/intelgt.h"
> +#include "cli/cli-cmds.h"
> +#include "dwarf2/frame.h"
> +#include "frame-unwind.h"
> +#include "gdbsupport/gdb_obstack.h"
gdb_obstack.h is unused.
> +/* Data specific for this architecture. */
> +
> +struct intelgt_gdbarch_tdep : gdbarch_tdep_base
> +{
> + /* $r0 GRF register number. */
> + int r0_regnum = -1;
> +
> + /* $ce register number in the regcache. */
> + int ce_regnum = -1;
> +
> + /* Register number for the GRF containing function return value. */
> + int retval_regnum = -1;
> +
> + /* Register number for the control register. */
> + int cr0_regnum = -1;
> +
> + /* Register number for the state register. */
> + int sr0_regnum = -1;
> +
> + /* Register number for the instruction base virtual register. */
> + int isabase_regnum = -1;
> +
> + /* Register number for the general state base SBA register. */
> + int genstbase_regnum = -1;
> +
> + /* Register number for the DBG0 register. */
> + int dbg0_regnum = -1;
> +
> + /* Assigned regnum ranges for DWARF regsets. */
> + regnum_range regset_ranges[intelgt::REGSET_COUNT];
> +
> + /* Enabled pseudo-registers for the current target description. */
> + std::vector<std::string> enabled_pseudo_regs;
> +
> + /* Cached $framedesc pseudo-register type. */
> + type *framedesc_type = nullptr;
> +
> + /* Initialize ranges to -1 as "not-yet-set" indicator. */
> + intelgt_gdbarch_tdep ()
> + {
> + memset (®set_ranges, -1, sizeof regset_ranges);
> + }
Instead of this memset, can you make it the default state of a
regnum_range?
struct regnum_range
{
int start = -1;
int end = -1;
};
Otherwise, I would prefer some loop that assigns {-1, -1} to each
field, rather than memset-ing objects.
Also, we usually put the constructors at the beginning...
> +
> + /* 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;
> + }
... and while we have a mixed bag of styles, I think that putting the
methods before the data members is common.
> +/* Read part of REGNUM at OFFSET into BUFFER. The length of data to
> + read is SIZE. Consider using this helper function when reading
> + subregisters of CR0, SR0, and R0. */
> +
> +static void
> +intelgt_read_register_part (readable_regcache *regcache, int regnum,
> + size_t offset,
> + gdb::array_view<gdb_byte> buffer,
> + const char *error_message)
> +{
> + if (regnum == -1)
> + error (_("%s Unexpected reg num '-1'."), error_message);
This should probably be an assert.
> +
> + gdbarch *arch = regcache->arch ();
> + const char *regname = gdbarch_register_name (arch, regnum);
> + int regsize = register_size (arch, regnum);
> +
> + if (offset + buffer.size () > regsize)
> + error (_("%s %s[%zu:%zu] is outside the range of %s[%d:0]."),
> + error_message, regname, (offset + buffer.size () - 1), offset,
> + regname, (regsize - 1));
I also wonder about this check, does it protect against bad user input,
or a bug elsewhere in GDB?
> +
> + register_status reg_status
> + = regcache->cooked_read_part (regnum, offset, buffer);
> +
> + if (reg_status == REG_UNAVAILABLE)
> + throw_error (NOT_AVAILABLE_ERROR,
> + _("%s Register %s (%d) is not available."),
> + error_message, regname, regnum);
> +
> + if (reg_status == REG_UNKNOWN)
> + error (_("%s Register %s (%d) is unknown."), error_message,
> + regname, regnum);
Does this REG_UNKNOWN case happen in real life? I always thought that
REG_UNKNOWN was the initial state of registers in the regcache, and that
after filling a regcache (a call to target_ops::fetch_registers), then
all registers would become either REG_VALID or REG_UNAVAILABLE. I
presumed that if a register was left in the REG_UNKNOWN, it would be a
target bug.
> +/* Convert a DWARF register number to a GDB register number. This
> + function requires the register listing in the target description to
> + be in the same order in each regset as the intended DWARF numbering
> + order. Currently this always holds true when gdbserver generates
> + the target description. */
> +
> +static int
> +intelgt_dwarf_reg_to_regnum (gdbarch *gdbarch, int num)
> +{
> + constexpr int ip = 0;
> + constexpr int ce = 1;
> +
> + /* Register sets follow this format: [START, END), where START is
> + inclusive and END is exclusive. */
> + constexpr regnum_range dwarf_nums[intelgt::REGSET_COUNT] = {
> + [intelgt::REGSET_SBA] = { 5, 12 },
> + [intelgt::REGSET_GRF] = { 16, 272 },
> + [intelgt::REGSET_ADDR] = { 272, 288 },
> + [intelgt::REGSET_FLAG] = { 288, 304 },
> + [intelgt::REGSET_ACC] = { 304, 320 },
> + [intelgt::REGSET_MME] = { 320, 336 },
> + };
> +
> + /* Number of SBA registers. */
> + constexpr size_t sba_dwarf_len = dwarf_nums[intelgt::REGSET_SBA].end
> + - dwarf_nums[intelgt::REGSET_SBA].start;
> +
> + /* Map the DWARF register numbers of SBA registers to their names.
> + Base number is dwarf_nums[intelgt::REGSET_SBA].start. */
> + constexpr const char* sba_dwarf_reg_order[sba_dwarf_len] {
Space before *.
> + "btbase",
> + "scrbase",
> + "genstbase",
> + "sustbase",
> + "blsustbase",
> + "blsastbase",
> + "scrbase2"
> + };
> +
> + intelgt_gdbarch_tdep *data
> + = gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch);
> +
> + if (num == ip)
> + return intelgt_pseudo_register_num (gdbarch, "ip");
> + if (num == ce)
> + return data->ce_regnum;
> +
> + for (int regset = 0; regset < intelgt::REGSET_COUNT; ++regset)
> + if (num >= dwarf_nums[regset].start && num < dwarf_nums[regset].end)
> + {
> + if (regset == intelgt::REGSET_SBA)
> + {
> + /* For SBA registers we first find out the name of the
> + register out of DWARF register number and then find the
> + register number corresponding to the name. */
> + int sba_num = num - dwarf_nums[intelgt::REGSET_SBA].start;
> + const char* name = sba_dwarf_reg_order [sba_num];
Space before *, no space before [.
> +
> + return user_reg_map_name_to_regnum (gdbarch, name, -1);
> + }
> + else
> + {
> + int candidate = data->regset_ranges[regset].start + num
> + - dwarf_nums[regset].start;
Formatting:
int candidate = (data->regset_ranges[regset].start + num
- dwarf_nums[regset].start);
> +/* The 'unwind_pc' gdbarch method. */
> +
> +static CORE_ADDR
> +intelgt_unwind_pc (gdbarch *gdbarch, const frame_info_ptr &next_frame)
> +{
> + /* Use ip register here, as IGC uses 32bit values (pc is 64bit). */
> + int ip_regnum = intelgt_pseudo_register_num (gdbarch, "ip");
> + CORE_ADDR prev_ip = frame_unwind_register_unsigned (next_frame,
> + ip_regnum);
> + intelgt_debug_printf ("prev_ip: %s", paddress (gdbarch, prev_ip));
> +
> + /* Program counter is $ip + $isabase. Read directly from the
> + regcache instead of unwinding, as the frame unwind info may
> + simply be unavailable. The isabase register does not change
> + during kernel execution, so this must be safe. */
> + regcache *regcache = get_thread_regcache (inferior_thread ());
> + CORE_ADDR isabase = intelgt_get_isabase (regcache);
Why do you not want to read isabase from NEXT_FRAME, because it's
slower? I'm asking because this use of inferior_thread slightly annoys
me.
> +/* The memory_insert_breakpoint gdbarch method. */
> +
> +static int
> +intelgt_memory_insert_breakpoint (gdbarch *gdbarch, bp_target_info *bp)
> +{
> + intelgt_debug_printf ("req ip: %s", paddress (gdbarch,
> + bp->reqstd_address));
> +
> + /* Ensure that we have enough space in the breakpoint. */
> + static_assert (intelgt::MAX_INST_LENGTH <= BREAKPOINT_MAX);
> +
> + gdb_byte inst[intelgt::MAX_INST_LENGTH];
> + int err = target_read_memory (bp->reqstd_address, inst,
> + intelgt::MAX_INST_LENGTH);
> + if (err != 0)
> + {
> + /* We could fall back to reading a full and then a compacted
> + instruction but I think we should rather allow short reads than
> + having the caller try smaller and smaller sizes. */
Please avoid the first person in comments (there is at least one other
instance).
> +/* The memory_remove_breakpoint gdbarch method. */
> +
> +static int
> +intelgt_memory_remove_breakpoint (gdbarch *gdbarch, struct bp_target_info *bp)
> +{
> + intelgt_debug_printf ("req ip: %s, placed ip: %s",
> + paddress (gdbarch, bp->reqstd_address),
> + paddress (gdbarch, bp->placed_address));
> +
> + /* Warn if we're inserting a permanent breakpoint. */
inserting -> removing?
> +/* The "read_pc" gdbarch method. */
> +
> +static CORE_ADDR
> +intelgt_read_pc (readable_regcache *regcache)
> +{
> + gdbarch *arch = regcache->arch ();
> + intelgt_gdbarch_tdep *data = gdbarch_tdep<intelgt_gdbarch_tdep> (arch);
> +
> + /* Instruction pointer is stored in CR0.2. */
> + uint32_t ip;
> + intelgt_read_register_part (regcache, data->cr0_regnum,
> + sizeof (uint32_t) * 2,
> + gdb::make_array_view ((gdb_byte *) &ip,
> + sizeof (uint32_t)),
> + _("Cannot compute PC."));
Even though we know the host will likely always be little-endian, this
is not technically correct. There should be an extract_unsigned_integer
call (or equivalent) to extract the target bytes into the host variable.
Same for other functions that read or update the PC using
make_array_view.
> +/* Called by tdesc_use_registers each time a new regnum
> + is assigned. Used to track down assigned numbers for
> + any important regnums. */
> +
> +static int
> +intelgt_unknown_register_cb (gdbarch *arch, tdesc_feature *feature,
> + const char *reg_name, int possible_regnum)
> +{
> + intelgt_gdbarch_tdep *data = gdbarch_tdep<intelgt_gdbarch_tdep> (arch);
> +
> + /* First, check if this a beginning of a not yet tracked regset
> + assignment. */
> +
> + for (int regset = 0; regset < intelgt::REGSET_COUNT; ++regset)
> + {
> + if (data->regset_ranges[regset].start == -1
> + && feature->name == intelgt::DWARF_REGSET_FEATURES[regset])
> + {
> + data->regset_ranges[regset].start = possible_regnum;
> + data->regset_ranges[regset].end
> + = feature->registers.size () + possible_regnum;
> + break;
> + }
> + }
You can get rid of the braces for the "for".
> +/* Architecture initialization. */
> +
> +static gdbarch *
> +intelgt_gdbarch_init (gdbarch_info info, gdbarch_list *arches)
> +{
> + /* If there is already a candidate, use it. */
> + arches = gdbarch_list_lookup_by_info (arches, &info);
> + if (arches != nullptr)
> + return arches->gdbarch;
> +
> + const target_desc *tdesc = info.target_desc;
> + gdbarch_up gdbarch_u
> + (gdbarch_alloc (&info,
> + gdbarch_tdep_up (new intelgt_gdbarch_tdep)));
> + gdbarch *gdbarch = gdbarch_u.get ();
> + intelgt_gdbarch_tdep *data
> + = gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch);
> +
> + /* 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);
Might as well inline the call to tdesc_data_alloc.
> +
> + /* 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"));
Missing "to be".
> + 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);
Opening parenthesis on next line.
Simon
^ permalink raw reply [flat|nested] 92+ messages in thread* RE: [PATCH v3 07/44] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture
2025-12-11 18:53 ` Simon Marchi
@ 2025-12-19 16:01 ` Aktemur, Tankut Baris
0 siblings, 0 replies; 92+ messages in thread
From: Aktemur, Tankut Baris @ 2025-12-19 16:01 UTC (permalink / raw)
To: Simon Marchi, gdb-patches, Metzger, Markus T
On Thursday, December 11, 2025 7:54 PM, Simon Marchi wrote:
> On 2025-08-01 05:37, 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.
> >
> > Unlike, for example, X86-64, IntelGT does not have a dedicated
> > breakpoint instruction. Instead, it has a breakpoint bit in each
> > instruction. Hence, any instruction can be used as a breakpoint
> > instruction.
> >
> > It further supports ignoring breakpoints for stepping over or resuming
> > from a breakpoint. This only works, of course, if we use breakpoint
> > bits inside the original instruction rather than replacing it with a
> > fixed breakpoint instruction.
> >
> > Add gdbarch methods for inserting and removing memory breakpoints by
> > setting and clearing those breakpoint bits.
> >
> > We define one pseudo-register, $framedesc, that provides a type alias
> > for the frame descriptor register in GRF, representing it as a struct.
> >
> > The value of the $pc register is the $ip register, which is provided
> > in $cr0.2, offset by $isabase.
> >
> > A translation function to map the device_id to a gen_version is
> > provided. This will be useful in various places, in particular to
> > generate the correct disassembly.
> >
> > Co-authored-by: Markus Metzger <markus.t.metzger@intel.com>
> > Co-authored-by: Natalia Saiapova <natalia.saiapova@intel.com>
> > Co-authored-by: Mihails Strasuns <mihails.strasuns@intel.com>
> > Co-authored-by: Mohamed Bouhaouel <mohamed.bouhaouel@intel.com>
>
> Only minor comments noted below.
>
> Approved-By: Simon Marchi <simon.marchi@efficios.com>
Thank you. Below I reply to some of your comments. Those that I omitted
have been addressed in the local branch. They will be included in the
next revision.
> > diff --git a/gdb/intelgt-tdep.c b/gdb/intelgt-tdep.c
...
> > +/* Read part of REGNUM at OFFSET into BUFFER. The length of data to
> > + read is SIZE. Consider using this helper function when reading
> > + subregisters of CR0, SR0, and R0. */
> > +
> > +static void
> > +intelgt_read_register_part (readable_regcache *regcache, int regnum,
> > + size_t offset,
> > + gdb::array_view<gdb_byte> buffer,
> > + const char *error_message)
> > +{
> > + if (regnum == -1)
> > + error (_("%s Unexpected reg num '-1'."), error_message);
>
> This should probably be an assert.
>
> > +
> > + gdbarch *arch = regcache->arch ();
> > + const char *regname = gdbarch_register_name (arch, regnum);
> > + int regsize = register_size (arch, regnum);
> > +
> > + if (offset + buffer.size () > regsize)
> > + error (_("%s %s[%zu:%zu] is outside the range of %s[%d:0]."),
> > + error_message, regname, (offset + buffer.size () - 1), offset,
> > + regname, (regsize - 1));
>
> I also wonder about this check, does it protect against bad user input,
> or a bug elsewhere in GDB?
Regarding this check and the check above:
Register sets are obtained from the debug API, which specifies the number
and the size of register sets. The errors here and above protect against
debug library bugs, in case it reports incomplete register sets or incorrect
information about the number/size of regs. So, we're checking against
input obtained from outside GDB.
> > +
> > + register_status reg_status
> > + = regcache->cooked_read_part (regnum, offset, buffer);
> > +
> > + if (reg_status == REG_UNAVAILABLE)
> > + throw_error (NOT_AVAILABLE_ERROR,
> > + _("%s Register %s (%d) is not available."),
> > + error_message, regname, regnum);
> > +
> > + if (reg_status == REG_UNKNOWN)
> > + error (_("%s Register %s (%d) is unknown."), error_message,
> > + regname, regnum);
>
> Does this REG_UNKNOWN case happen in real life? I always thought that
> REG_UNKNOWN was the initial state of registers in the regcache, and that
> after filling a regcache (a call to target_ops::fetch_registers), then
> all registers would become either REG_VALID or REG_UNAVAILABLE. I
> presumed that if a register was left in the REG_UNKNOWN, it would be a
> target bug.
I don't think this case ever happened in real life. We included the check
for completeness' sake.
...
> > +/* The 'unwind_pc' gdbarch method. */
> > +
> > +static CORE_ADDR
> > +intelgt_unwind_pc (gdbarch *gdbarch, const frame_info_ptr
> &next_frame)
> > +{
> > + /* Use ip register here, as IGC uses 32bit values (pc is 64bit).
> */
> > + int ip_regnum = intelgt_pseudo_register_num (gdbarch, "ip");
> > + CORE_ADDR prev_ip = frame_unwind_register_unsigned (next_frame,
> > + ip_regnum);
> > + intelgt_debug_printf ("prev_ip: %s", paddress (gdbarch, prev_ip));
> > +
> > + /* Program counter is $ip + $isabase. Read directly from the
> > + regcache instead of unwinding, as the frame unwind info may
> > + simply be unavailable. The isabase register does not change
> > + during kernel execution, so this must be safe. */
> > + regcache *regcache = get_thread_regcache (inferior_thread ());
> > + CORE_ADDR isabase = intelgt_get_isabase (regcache);
>
> Why do you not want to read isabase from NEXT_FRAME, because it's
> slower? I'm asking because this use of inferior_thread slightly annoys
> me.
`isabase` is a virtual register. Compiler does not emit unwind information
for it. However, GDB is able to cope with that. The register would be
eventually read from regcache. We can replace this part with
frame_unwind_register_unsigned to get rid of `inferior_thread ()`.
...
> > +/* The memory_remove_breakpoint gdbarch method. */
> > +
> > +static int
> > +intelgt_memory_remove_breakpoint (gdbarch *gdbarch, struct
> bp_target_info *bp)
> > +{
> > + intelgt_debug_printf ("req ip: %s, placed ip: %s",
> > + paddress (gdbarch, bp->reqstd_address),
> > + paddress (gdbarch, bp->placed_address));
> > +
> > + /* Warn if we're inserting a permanent breakpoint. */
>
> inserting -> removing?
Here, we are inserting the original contents of the instruction
and raising a warning if it had a permanent breakpoint. I agree,
however, that saying "inserting" in this function sounds confusing.
I thought about rephrasing the comment, but both the original comment
and my rewording would be repeating what the code is actually doing.
So, I removed the comment.
-Baris
Intel Deutschland GmbH
Registered Address: Dornacher Straße 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht München HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread
* [PATCH v3 08/44] gdb, intelgt: add disassemble feature for the Intel GT architecture.
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (6 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 07/44] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-12-11 19:37 ` Simon Marchi
2025-08-01 9:37 ` [PATCH v3 09/44] gdb, gdbserver, ze: in-memory libraries Tankut Baris Aktemur
` (38 subsequent siblings)
46 siblings, 1 reply; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
From: Albertano Caruso <albertano.caruso@intel.com>
Disassembly of Intel GT programs is done via the Intel Gen Assembler
library. Add this library as an optional dependency. If the library
is not found, disassembly is disabled.
---
gdb/Makefile.in | 6 +-
gdb/README | 8 +
gdb/config.in | 3 +
gdb/configure | 559 ++++++++++++++++++++++++++++++++++++++++++++++++-
gdb/configure.ac | 52 +++++
gdb/disasm-selftests.c | 12 ++
gdb/doc/gdb.texinfo | 5 +
gdb/intelgt-tdep.c | 113 +++++++++-
gdb/top.c | 10 +
9 files changed, 764 insertions(+), 4 deletions(-)
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index a6f32b5c02877592ff3072a431b7e229e031ebde..89e613e705963261906fb3bdaa5a3400b2e618e6 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
@@ -684,7 +688,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/README b/gdb/README
index eca4181c751eb3ff74dae505e98ed2826ebf2f27..9f1192f591967e81a7dc155f5a4cec1e8a89cb9f 100644
--- a/gdb/README
+++ b/gdb/README
@@ -500,6 +500,14 @@ more obscure GDB `configure' options are not listed here.
the overall build. See the GDB manual instructions on how to do
this.
+`--with-libiga64`
+ Build GDB with Intel Gen Assembler (IGA) library. If requested and
+ the IntelGT target is enabled but the library cannot be found,
+ configure gives an error. If not requested explicitly and cannot
+ be found, disassembly feature for the IntelGT target will not be
+ available in GDB. IGA is available as part of the Intel Graphics
+ Compiler: `https://github.com/intel/intel-graphics-compiler`
+
`--with-lzma'
Build GDB with LZMA, a compression library. (Done by default if
liblzma is installed and found at configure time.) LZMA is used
diff --git a/gdb/config.in b/gdb/config.in
index 149aeaf979bafe4eb8248d9ba0bdefe13a761de8..103fcf8e8bc044e4c72ae3046a0d84491f78845f 100644
--- a/gdb/config.in
+++ b/gdb/config.in
@@ -283,6 +283,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 8fc3b04efbfc2323c1bc9690a9ec7b10442ed5b1..b4120f4eefbe527c1b8d2ac0978327e2bb9a6851 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
@@ -957,6 +960,9 @@ with_python_libdir
with_guile
enable_gdb_compile
enable_source_highlight
+with_libiga64
+with_libiga64_prefix
+with_libiga64_type
with_sysroot
with_system_gdbinit
with_system_gdbinit_dir
@@ -1736,6 +1742,10 @@ Optional Packages:
search for python's libraries in DIR
--with-guile[=GUILE] include guile support
(auto/yes/no/<guile-version>/<pkg-config-program>)
+ --with-libiga64 include IntelGT disassembly support (auto/yes/no)
+ --with-libiga64-prefix[=DIR] search for libiga64 in DIR/include and DIR/lib
+ --without-libiga64-prefix don't search for libiga64 in includedir and libdir
+ --with-libiga64-type=TYPE type of library to search for (auto/static/shared)
--with-sysroot[=DIR] search for usr/lib et al within DIR
--with-system-gdbinit=PATH
automatically load a system-wide gdbinit file
@@ -11507,7 +11517,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
-#line 11510 "configure"
+#line 11520 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@@ -11613,7 +11623,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
-#line 11616 "configure"
+#line 11626 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@@ -29146,6 +29156,551 @@ fi
+# Check for Intel(R) Graphics Technology assembler library
+intelgt_target=false
+
+for targ_alias in `echo $target_alias $enable_targets | sed 's/,/ /g'`
+do
+ if test "$targ_alias" = "all"; then
+ intelgt_target=true
+ else
+ case "$targ_alias" in
+ intelgt-*)
+ intelgt_target=true
+ ;;
+ esac
+ fi
+done
+
+case "${target}" in
+ intelgt-*)
+ intelgt_target=true
+ ;;
+esac
+
+if test "${intelgt_target}" != true; then
+ HAVE_LIBIGA64=no
+else
+
+# Check whether --with-libiga64 was given.
+if test "${with_libiga64+set}" = set; then :
+ withval=$with_libiga64;
+else
+ with_libiga64=auto
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use libiga64" >&5
+$as_echo_n "checking whether to use libiga64... " >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_libiga64" >&5
+$as_echo "$with_libiga64" >&6; }
+
+ if test "${with_libiga64}" = no; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: IntelGT disassembly support disabled; some features may be unavailable." >&5
+$as_echo "$as_me: WARNING: IntelGT disassembly support disabled; some features may be unavailable." >&2;}
+ HAVE_LIBIGA64=no
+ else
+ ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+
+
+
+
+
+
+
+
+ use_additional=yes
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+
+# Check whether --with-libiga64-prefix was given.
+if test "${with_libiga64_prefix+set}" = set; then :
+ withval=$with_libiga64_prefix;
+ if test "X$withval" = "Xno"; then
+ use_additional=no
+ else
+ if test "X$withval" = "X"; then
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ else
+ additional_includedir="$withval/include"
+ additional_libdir="$withval/lib"
+ fi
+ fi
+
+fi
+
+
+# Check whether --with-libiga64-type was given.
+if test "${with_libiga64_type+set}" = set; then :
+ withval=$with_libiga64_type; with_libiga64_type=$withval
+else
+ with_libiga64_type=auto
+fi
+
+ lib_type=`eval echo \$with_libiga64_type`
+
+ LIBIGA64=
+ LTLIBIGA64=
+ INCIGA64=
+ rpathdirs=
+ ltrpathdirs=
+ names_already_handled=
+ names_next_round='iga64 '
+ while test -n "$names_next_round"; do
+ names_this_round="$names_next_round"
+ names_next_round=
+ for name in $names_this_round; do
+ already_handled=
+ for n in $names_already_handled; do
+ if test "$n" = "$name"; then
+ already_handled=yes
+ break
+ fi
+ done
+ if test -z "$already_handled"; then
+ names_already_handled="$names_already_handled $name"
+ uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
+ eval value=\"\$HAVE_LIB$uppername\"
+ if test -n "$value"; then
+ if test "$value" = yes; then
+ eval value=\"\$LIB$uppername\"
+ test -z "$value" || LIBIGA64="${LIBIGA64}${LIBIGA64:+ }$value"
+ eval value=\"\$LTLIB$uppername\"
+ test -z "$value" || LTLIBIGA64="${LTLIBIGA64}${LTLIBIGA64:+ }$value"
+ else
+ :
+ fi
+ else
+ found_dir=
+ found_la=
+ found_so=
+ found_a=
+ if test $use_additional = yes; then
+ if test -n "$shlibext" && test -f "$additional_libdir/lib$name.$shlibext" && test x$lib_type != xstatic; then
+ found_dir="$additional_libdir"
+ found_so="$additional_libdir/lib$name.$shlibext"
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ elif test x$lib_type != xshared; then
+ if test -f "$additional_libdir/lib$name.$libext"; then
+ found_dir="$additional_libdir"
+ found_a="$additional_libdir/lib$name.$libext"
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ fi
+ fi
+ fi
+ if test "X$found_dir" = "X"; then
+ for x in $LDFLAGS $LTLIBIGA64; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ case "$x" in
+ -L*)
+ dir=`echo "X$x" | sed -e 's/^X-L//'`
+ if test -n "$shlibext" && test -f "$dir/lib$name.$shlibext" && test x$lib_type != xstatic; then
+ found_dir="$dir"
+ found_so="$dir/lib$name.$shlibext"
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ elif test x$lib_type != xshared; then
+ if test -f "$dir/lib$name.$libext"; then
+ found_dir="$dir"
+ found_a="$dir/lib$name.$libext"
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ fi
+ fi
+ ;;
+ esac
+ if test "X$found_dir" != "X"; then
+ break
+ fi
+ done
+ fi
+ if test "X$found_dir" != "X"; then
+ LTLIBIGA64="${LTLIBIGA64}${LTLIBIGA64:+ }-L$found_dir -l$name"
+ if test "X$found_so" != "X"; then
+ if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/lib"; then
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }$found_so"
+ else
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $found_dir"
+ fi
+ if test "$hardcode_direct" = yes; then
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }$found_so"
+ else
+ if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }$found_so"
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $found_dir"
+ fi
+ else
+ haveit=
+ for x in $LDFLAGS $LIBIGA64; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }-L$found_dir"
+ fi
+ if test "$hardcode_minus_L" != no; then
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }$found_so"
+ else
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }-l$name"
+ fi
+ fi
+ fi
+ fi
+ else
+ if test "X$found_a" != "X"; then
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }$found_a"
+ else
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }-L$found_dir -l$name"
+ fi
+ fi
+ additional_includedir=
+ case "$found_dir" in
+ */lib | */lib/)
+ basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e 's,/lib/*$,,'`
+ additional_includedir="$basedir/include"
+ ;;
+ esac
+ if test "X$additional_includedir" != "X"; then
+ if test "X$additional_includedir" != "X/usr/include"; then
+ haveit=
+ if test "X$additional_includedir" = "X/usr/local/include"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux*) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ for x in $CPPFLAGS $INCIGA64; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-I$additional_includedir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_includedir"; then
+ INCIGA64="${INCIGA64}${INCIGA64:+ }-I$additional_includedir"
+ fi
+ fi
+ fi
+ fi
+ fi
+ if test -n "$found_la"; then
+ save_libdir="$libdir"
+ case "$found_la" in
+ */* | *\\*) . "$found_la" ;;
+ *) . "./$found_la" ;;
+ esac
+ libdir="$save_libdir"
+ for dep in $dependency_libs; do
+ case "$dep" in
+ -L*)
+ additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
+ if test "X$additional_libdir" != "X/usr/lib"; then
+ haveit=
+ if test "X$additional_libdir" = "X/usr/local/lib"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux*) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ haveit=
+ for x in $LDFLAGS $LIBIGA64; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }-L$additional_libdir"
+ fi
+ fi
+ haveit=
+ for x in $LDFLAGS $LTLIBIGA64; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ LTLIBIGA64="${LTLIBIGA64}${LTLIBIGA64:+ }-L$additional_libdir"
+ fi
+ fi
+ fi
+ fi
+ ;;
+ -R*)
+ dir=`echo "X$dep" | sed -e 's/^X-R//'`
+ if test "$enable_rpath" != no; then
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $dir"
+ fi
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $dir"
+ fi
+ fi
+ ;;
+ -l*)
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'`
+ ;;
+ *.la)
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
+ ;;
+ *)
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }$dep"
+ LTLIBIGA64="${LTLIBIGA64}${LTLIBIGA64:+ }$dep"
+ ;;
+ esac
+ done
+ fi
+ else
+ if test "x$lib_type" = "xauto" || test "x$lib_type" = "xshared"; then
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }-l$name"
+ LTLIBIGA64="${LTLIBIGA64}${LTLIBIGA64:+ }-l$name"
+ else
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }-l:lib$name.$libext"
+ LTLIBIGA64="${LTLIBIGA64}${LTLIBIGA64:+ }-l:lib$name.$libext"
+ fi
+ fi
+ fi
+ fi
+ done
+ done
+ if test "X$rpathdirs" != "X"; then
+ if test -n "$hardcode_libdir_separator"; then
+ alldirs=
+ for found_dir in $rpathdirs; do
+ alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir"
+ done
+ acl_save_libdir="$libdir"
+ libdir="$alldirs"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }$flag"
+ else
+ for found_dir in $rpathdirs; do
+ acl_save_libdir="$libdir"
+ libdir="$found_dir"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }$flag"
+ done
+ fi
+ fi
+ if test "X$ltrpathdirs" != "X"; then
+ for found_dir in $ltrpathdirs; do
+ LTLIBIGA64="${LTLIBIGA64}${LTLIBIGA64:+ }-R$found_dir"
+ done
+ fi
+
+
+ ac_save_CPPFLAGS="$CPPFLAGS"
+
+ for element in $INCIGA64; do
+ haveit=
+ for x in $CPPFLAGS; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X$element"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element"
+ fi
+ done
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libiga64" >&5
+$as_echo_n "checking for libiga64... " >&6; }
+if ${ac_cv_libiga64+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ ac_save_LIBS="$LIBS"
+ LIBS="$LIBS $LIBIGA64"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include "assert.h"
+ #include "iga/iga.h"
+int
+main ()
+{
+iga_version_string();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+ ac_cv_libiga64=yes
+else
+ ac_cv_libiga64=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LIBS="$ac_save_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_libiga64" >&5
+$as_echo "$ac_cv_libiga64" >&6; }
+ if test "$ac_cv_libiga64" = yes; then
+ HAVE_LIBIGA64=yes
+
+$as_echo "#define HAVE_LIBIGA64 1" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libiga64" >&5
+$as_echo_n "checking how to link with libiga64... " >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBIGA64" >&5
+$as_echo "$LIBIGA64" >&6; }
+ else
+ HAVE_LIBIGA64=no
+ CPPFLAGS="$ac_save_CPPFLAGS"
+ LIBIGA64=
+ LTLIBIGA64=
+ fi
+
+
+
+
+
+
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+ if test "$HAVE_LIBIGA64" != yes; then
+ if test "$with_libiga64" = yes; then
+ as_fn_error $? "libiga64 is missing or unusable" "$LINENO" 5
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libiga64 is missing or unusable; some features may be unavailable." >&5
+$as_echo "$as_me: WARNING: libiga64 is missing or unusable; some features may be unavailable." >&2;}
+ fi
+ fi
+ fi
+fi
+
# ------------------------- #
# Checks for header files. #
# ------------------------- #
diff --git a/gdb/configure.ac b/gdb/configure.ac
index 226e27e4fe54a9d3ac06e8be4439dd5dce8eb975..0507f03043c61385181b667d0e7117d4b2547d6d 100644
--- a/gdb/configure.ac
+++ b/gdb/configure.ac
@@ -1351,6 +1351,58 @@ fi
AC_SUBST(SRCHIGH_LIBS)
AC_SUBST(SRCHIGH_CFLAGS)
+# Check for Intel(R) Graphics Technology assembler library
+intelgt_target=false
+
+for targ_alias in `echo $target_alias $enable_targets | sed 's/,/ /g'`
+do
+ if test "$targ_alias" = "all"; then
+ intelgt_target=true
+ else
+ case "$targ_alias" in
+ intelgt-*)
+ intelgt_target=true
+ ;;
+ esac
+ fi
+done
+
+case "${target}" in
+ intelgt-*)
+ intelgt_target=true
+ ;;
+esac
+
+if test "${intelgt_target}" != true; then
+ HAVE_LIBIGA64=no
+else
+ AC_ARG_WITH(libiga64,
+ AS_HELP_STRING([--with-libiga64], [include IntelGT disassembly support (auto/yes/no)]),
+ [], [with_libiga64=auto])
+ AC_MSG_CHECKING([whether to use libiga64])
+ AC_MSG_RESULT([$with_libiga64])
+
+ if test "${with_libiga64}" = no; then
+ AC_MSG_WARN([IntelGT disassembly support disabled; some features may be unavailable.])
+ HAVE_LIBIGA64=no
+ else
+ AC_LANG_PUSH([C++])
+ AC_LIB_HAVE_LINKFLAGS([iga64], [],
+ [#include "assert.h"
+ #include "iga/iga.h"],
+ [iga_version_string();])
+ AC_LANG_POP([C++])
+
+ if test "$HAVE_LIBIGA64" != yes; then
+ if test "$with_libiga64" = yes; then
+ AC_MSG_ERROR([libiga64 is missing or unusable])
+ else
+ AC_MSG_WARN([libiga64 is missing or unusable; some features may be unavailable.])
+ fi
+ fi
+ fi
+fi
+
# ------------------------- #
# Checks for header files. #
# ------------------------- #
diff --git a/gdb/disasm-selftests.c b/gdb/disasm-selftests.c
index 0d060654da10ec49c5a71e0009391e400d3d66ef..607f12bdc67ab051d212183a100ae29c93a0cdac 100644
--- a/gdb/disasm-selftests.c
+++ b/gdb/disasm-selftests.c
@@ -116,6 +116,10 @@ get_test_insn (struct gdbarch *gdbarch, size_t *len)
*len = bplen;
}
break;
+ case bfd_arch_intelgt:
+ /* The intelgt architecture needs to initialize the gdbarch with
+ an IGA context to be able to use the disassembler. */
+ return insn;
case bfd_arch_i386:
{
const struct bfd_arch_info *info = gdbarch_bfd_arch_info (gdbarch);
@@ -313,6 +317,14 @@ memory_error_test (struct gdbarch *gdbarch)
return;
}
+#if !defined (HAVE_LIBIGA64)
+ if (gdbarch_bfd_arch_info (gdbarch)->arch == bfd_arch_intelgt)
+ {
+ /* Disassembler for intelgt requires libiga64. */
+ return;
+ }
+#endif
+
gdb_disassembler_test di (gdbarch);
bool saw_memory_error = false;
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 3d5b4491f775866d806782a92e0aa76c3539ff25..a4c98d18e20182765f8cf0546447c6777cdd5e2d 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -40984,6 +40984,11 @@ to download GNU Libiconv, unpack it inside the top-level directory of
the @value{GDBN} source tree, and then rename the directory holding
the Libiconv source code to @samp{libiconv}.
+@item libiga64 (IGA)
+@value{GDBN} can disassemble IntelGT machine code if this library is
+included. Use the @option{--with-libiga64-prefix} option to specify
+its non-standard location.
+
@cindex compressed debug sections
@item lzma
@value{GDBN} can support debugging sections that are compressed with
diff --git a/gdb/intelgt-tdep.c b/gdb/intelgt-tdep.c
index 3919947fed44c9e939bef6c14b854ae416ab7c41..583847dd168b39ff68036c91e1c63d7290988a7a 100644
--- a/gdb/intelgt-tdep.c
+++ b/gdb/intelgt-tdep.c
@@ -31,6 +31,10 @@
#include "inferior.h"
#include "user-regs.h"
#include <algorithm>
+#include "disasm.h"
+#if defined (HAVE_LIBIGA64)
+#include "iga/iga.h"
+#endif /* defined (HAVE_LIBIGA64) */
/* Global debug flag. */
static bool intelgt_debug = false;
@@ -99,6 +103,11 @@ struct intelgt_gdbarch_tdep : gdbarch_tdep_base
gdb_assert (regset_ranges[intelgt::REGSET_GRF].end > 1);
return regset_ranges[intelgt::REGSET_GRF].end - 1;
}
+
+#if defined (HAVE_LIBIGA64)
+ /* libiga context for disassembly. */
+ iga_context_t iga_ctx = nullptr;
+#endif
};
/* Per-inferior cached data for the Intelgt target. */
@@ -544,13 +553,75 @@ intelgt_sw_breakpoint_from_kind (gdbarch *gdbarch, int kind, int *size)
return nullptr;
}
+#if defined (HAVE_LIBIGA64)
+/* Map CORE_ADDR to symbol names for jump labels in an IGA disassembly. */
+
+static const char *
+intelgt_disasm_sym_cb (int addr, void *ctx)
+{
+ disassemble_info *info = (disassemble_info *) ctx;
+ symbol *sym = find_pc_function (addr + (uintptr_t) info->private_data);
+ return sym ? sym->linkage_name () : nullptr;
+}
+#endif /* defined (HAVE_LIBIGA64) */
+
/* Print one instruction from MEMADDR on INFO->STREAM. */
static int
intelgt_print_insn (bfd_vma memaddr, struct disassemble_info *info)
{
- /* Disassembler is to be added in a later patch. */
+#if !defined (HAVE_LIBIGA64)
+ gdb_printf (_("\nDisassemble feature not available: libiga64 "
+ "is missing.\n"));
return -1;
+#else
+ std::unique_ptr<bfd_byte[]> insn (new bfd_byte[intelgt::MAX_INST_LENGTH]);
+
+ int status = (*info->read_memory_func) (memaddr, insn.get (),
+ intelgt::COMPACT_INST_LENGTH, info);
+ if (status != 0)
+ {
+ /* Aborts disassembling with a memory_error exception. */
+ (*info->memory_error_func) (status, memaddr, info);
+ return -1;
+ }
+
+ uint32_t device_id = get_device_id (current_inferior ());
+ gdb::array_view<bfd_byte> insn_view
+ = gdb::make_array_view (insn.get (), intelgt::COMPACT_INST_LENGTH);
+ unsigned int length = intelgt::inst_length (insn_view, device_id);
+
+ if (length == intelgt::MAX_INST_LENGTH)
+ {
+ status = (*info->read_memory_func) (memaddr, insn.get (),
+ intelgt::MAX_INST_LENGTH, info);
+ if (status != 0)
+ {
+ /* Aborts disassembling with a memory_error exception. */
+ (*info->memory_error_func) (status, memaddr, info);
+ return -1;
+ }
+ }
+
+ char *dbuf;
+ iga_disassemble_options_t dopts = IGA_DISASSEMBLE_OPTIONS_INIT ();
+ gdb_disassemble_info *di
+ = static_cast<gdb_disassemble_info *>(info->application_data);
+ struct gdbarch *gdbarch = di->arch ();
+
+ iga_context_t iga_ctx
+ = gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch)->iga_ctx;
+ iga_status_t iga_status
+ = iga_context_disassemble_instruction (iga_ctx, &dopts, insn.get (),
+ intelgt_disasm_sym_cb,
+ info, &dbuf);
+ if (iga_status != IGA_SUCCESS)
+ return -1;
+
+ (*info->fprintf_func) (info->stream, "%s", dbuf);
+
+ return length;
+#endif /* defined (HAVE_LIBIGA64) */
}
/* The "read_pc" gdbarch method. */
@@ -771,6 +842,46 @@ intelgt_gdbarch_init (gdbarch_info info, gdbarch_list *arches)
intelgt_gdbarch_tdep *data
= gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch);
+#if defined (HAVE_LIBIGA64)
+ iga_gen_t iga_version = IGA_GEN_INVALID;
+
+ if (tdesc != nullptr)
+ {
+ const tdesc_device *device_info = tdesc_device_info (tdesc);
+ if (!(device_info->vendor_id.has_value ()
+ && device_info->target_id.has_value ()))
+ {
+ warning (_("Device vendor id and target id not found."));
+ gdbarch_free (gdbarch);
+ return nullptr;
+ }
+
+ uint32_t vendor_id = *device_info->vendor_id;
+ uint32_t device_id = *device_info->target_id;
+ if (vendor_id != 0x8086)
+ {
+ warning (_("Device not recognized: vendor id=0x%04x,"
+ " device id=0x%04x"), vendor_id, device_id);
+ gdbarch_free (gdbarch);
+ return nullptr;
+ }
+ else
+ {
+ iga_version = (iga_gen_t) intelgt::get_xe_version (device_id);
+ if (iga_version == IGA_GEN_INVALID)
+ warning (_("Intel GT device id is unrecognized: ID 0x%04x"),
+ device_id);
+ }
+ }
+
+ /* Take the best guess in case IGA_VERSION is still invalid. */
+ if (iga_version == IGA_GEN_INVALID)
+ iga_version = IGA_XE_HPC;
+
+ const iga_context_options_t options = IGA_CONTEXT_OPTIONS_INIT (iga_version);
+ iga_context_create (&options, &data->iga_ctx);
+#endif
+
/* Initialize register info. */
set_gdbarch_num_regs (gdbarch, 0);
set_gdbarch_register_name (gdbarch, tdesc_register_name);
diff --git a/gdb/top.c b/gdb/top.c
index 72d19530070ea0c7cec0647cf643bb0556e3a37b..25576d28964f61915775fe62594207f68d2ed716 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -1523,6 +1523,16 @@ This GDB was configured as follows:\n\
"));
#endif
+#ifdef HAVE_LIBIGA64
+ gdb_printf (stream, _("\
+ --with-libiga64\n\
+"));
+#else
+ gdb_printf (stream, _("\
+ --without-libiga64\n\
+"));
+#endif
+
#if HAVE_SOURCE_HIGHLIGHT
gdb_printf (stream, _("\
--enable-source-highlight\n\
--
2.34.1
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread* Re: [PATCH v3 08/44] gdb, intelgt: add disassemble feature for the Intel GT architecture.
2025-08-01 9:37 ` [PATCH v3 08/44] gdb, intelgt: add disassemble feature " Tankut Baris Aktemur
@ 2025-12-11 19:37 ` Simon Marchi
2025-12-23 11:03 ` Aktemur, Tankut Baris
0 siblings, 1 reply; 92+ messages in thread
From: Simon Marchi @ 2025-12-11 19:37 UTC (permalink / raw)
To: Tankut Baris Aktemur, gdb-patches, Markus Metzger
> diff --git a/gdb/configure.ac b/gdb/configure.ac
> index 226e27e4fe54a9d3ac06e8be4439dd5dce8eb975..0507f03043c61385181b667d0e7117d4b2547d6d 100644
> --- a/gdb/configure.ac
> +++ b/gdb/configure.ac
> @@ -1351,6 +1351,58 @@ fi
> AC_SUBST(SRCHIGH_LIBS)
> AC_SUBST(SRCHIGH_CFLAGS)
>
> +# Check for Intel(R) Graphics Technology assembler library
> +intelgt_target=false
> +
> +for targ_alias in `echo $target_alias $enable_targets | sed 's/,/ /g'`
> +do
> + if test "$targ_alias" = "all"; then
> + intelgt_target=true
> + else
> + case "$targ_alias" in
> + intelgt-*)
> + intelgt_target=true
> + ;;
> + esac
> + fi
> +done
> +
> +case "${target}" in
> + intelgt-*)
> + intelgt_target=true
> + ;;
> +esac
Do you really need this last "case"? I would think that the main target
would be included in the loop above (as $target_alias).
> +
> +if test "${intelgt_target}" != true; then
> + HAVE_LIBIGA64=no
> +else
> + AC_ARG_WITH(libiga64,
> + AS_HELP_STRING([--with-libiga64], [include IntelGT disassembly support (auto/yes/no)]),
> + [], [with_libiga64=auto])
Not an autoconf expert, but I think it makes more sense (and is clearer)
to put the AC_ARG_WITH at the top-level. The --with-libiga64 flag will
always be there, it's not like it will be predicated by $intelgt_target.
> @@ -544,13 +553,75 @@ intelgt_sw_breakpoint_from_kind (gdbarch *gdbarch, int kind, int *size)
> return nullptr;
> }
>
> +#if defined (HAVE_LIBIGA64)
> +/* Map CORE_ADDR to symbol names for jump labels in an IGA disassembly. */
> +
> +static const char *
> +intelgt_disasm_sym_cb (int addr, void *ctx)
> +{
> + disassemble_info *info = (disassemble_info *) ctx;
> + symbol *sym = find_pc_function (addr + (uintptr_t) info->private_data);
> + return sym ? sym->linkage_name () : nullptr;
> +}
> +#endif /* defined (HAVE_LIBIGA64) */
> +
> /* Print one instruction from MEMADDR on INFO->STREAM. */
>
> static int
> intelgt_print_insn (bfd_vma memaddr, struct disassemble_info *info)
> {
> - /* Disassembler is to be added in a later patch. */
> +#if !defined (HAVE_LIBIGA64)
> + gdb_printf (_("\nDisassemble feature not available: libiga64 "
> + "is missing.\n"));
> return -1;
> +#else
> + std::unique_ptr<bfd_byte[]> insn (new bfd_byte[intelgt::MAX_INST_LENGTH]);
Can this be statically allocated?
bfd_byte insn[intelgt::MAX_INST_LENGTH];
> +
> + int status = (*info->read_memory_func) (memaddr, insn.get (),
> + intelgt::COMPACT_INST_LENGTH, info);
> + if (status != 0)
> + {
> + /* Aborts disassembling with a memory_error exception. */
> + (*info->memory_error_func) (status, memaddr, info);
> + return -1;
> + }
> +
> + uint32_t device_id = get_device_id (current_inferior ());
> + gdb::array_view<bfd_byte> insn_view
> + = gdb::make_array_view (insn.get (), intelgt::COMPACT_INST_LENGTH);
> + unsigned int length = intelgt::inst_length (insn_view, device_id);
> +
> + if (length == intelgt::MAX_INST_LENGTH)
> + {
> + status = (*info->read_memory_func) (memaddr, insn.get (),
> + intelgt::MAX_INST_LENGTH, info);
> + if (status != 0)
> + {
> + /* Aborts disassembling with a memory_error exception. */
> + (*info->memory_error_func) (status, memaddr, info);
> + return -1;
> + }
> + }
> +
> + char *dbuf;
> + iga_disassemble_options_t dopts = IGA_DISASSEMBLE_OPTIONS_INIT ();
> + gdb_disassemble_info *di
> + = static_cast<gdb_disassemble_info *>(info->application_data);
Space before paren.
> @@ -771,6 +842,46 @@ intelgt_gdbarch_init (gdbarch_info info, gdbarch_list *arches)
> intelgt_gdbarch_tdep *data
> = gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch);
>
> +#if defined (HAVE_LIBIGA64)
> + iga_gen_t iga_version = IGA_GEN_INVALID;
> +
> + if (tdesc != nullptr)
> + {
> + const tdesc_device *device_info = tdesc_device_info (tdesc);
> + if (!(device_info->vendor_id.has_value ()
> + && device_info->target_id.has_value ()))
> + {
> + warning (_("Device vendor id and target id not found."));
Perhaps say "... not found in target description."? Trying to think
about a clueless user seeing this message without much context.
> + 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;
I don't think these two gdbarch_free are needed, that is managed by
gdbarch_u.
> + }
> + else
I would remove this "else" (just de-indent the code below).
> + {
> + iga_version = (iga_gen_t) intelgt::get_xe_version (device_id);
> + if (iga_version == IGA_GEN_INVALID)
> + warning (_("Intel GT device id is unrecognized: ID 0x%04x"),
> + device_id);
> + }
> + }
> +
> + /* Take the best guess in case IGA_VERSION is still invalid. */
> + if (iga_version == IGA_GEN_INVALID)
> + iga_version = IGA_XE_HPC;
> +
> + const iga_context_options_t options = IGA_CONTEXT_OPTIONS_INIT (iga_version);
> + iga_context_create (&options, &data->iga_ctx);
Should probably check the return value of iga_context_create.
Simon
^ permalink raw reply [flat|nested] 92+ messages in thread* RE: [PATCH v3 08/44] gdb, intelgt: add disassemble feature for the Intel GT architecture.
2025-12-11 19:37 ` Simon Marchi
@ 2025-12-23 11:03 ` Aktemur, Tankut Baris
0 siblings, 0 replies; 92+ messages in thread
From: Aktemur, Tankut Baris @ 2025-12-23 11:03 UTC (permalink / raw)
To: Simon Marchi, gdb-patches, Metzger, Markus T
Hi Simon,
On Thursday, December 11, 2025 8:37 PM, Simon Marchi wrote:
> > diff --git a/gdb/configure.ac b/gdb/configure.ac
> > index
> 226e27e4fe54a9d3ac06e8be4439dd5dce8eb975..0507f03043c61385181b667d0e7117
> d4b2547d6d 100644
> > --- a/gdb/configure.ac
> > +++ b/gdb/configure.ac
> > @@ -1351,6 +1351,58 @@ fi
> > AC_SUBST(SRCHIGH_LIBS)
> > AC_SUBST(SRCHIGH_CFLAGS)
> >
> > +# Check for Intel(R) Graphics Technology assembler library
> > +intelgt_target=false
> > +
> > +for targ_alias in `echo $target_alias $enable_targets | sed 's/,/
> /g'`
> > +do
> > + if test "$targ_alias" = "all"; then
> > + intelgt_target=true
> > + else
> > + case "$targ_alias" in
> > + intelgt-*)
> > + intelgt_target=true
> > + ;;
> > + esac
> > + fi
> > +done
> > +
> > +case "${target}" in
> > + intelgt-*)
> > + intelgt_target=true
> > + ;;
> > +esac
>
> Do you really need this last "case"? I would think that the main target
> would be included in the loop above (as $target_alias).
No, this shouldn't be needed. We'll remove it in the next revision.
> > +
> > +if test "${intelgt_target}" != true; then
> > + HAVE_LIBIGA64=no
> > +else
> > + AC_ARG_WITH(libiga64,
> > + AS_HELP_STRING([--with-libiga64], [include IntelGT disassembly
> support (auto/yes/no)]),
> > + [], [with_libiga64=auto])
>
> Not an autoconf expert, but I think it makes more sense (and is clearer)
> to put the AC_ARG_WITH at the top-level. The --with-libiga64 flag will
> always be there, it's not like it will be predicated by $intelgt_target.
We'll move it outside 'else'.
> > @@ -544,13 +553,75 @@ intelgt_sw_breakpoint_from_kind (gdbarch
> *gdbarch, int kind, int *size)
> > return nullptr;
> > }
> >
> > +#if defined (HAVE_LIBIGA64)
> > +/* Map CORE_ADDR to symbol names for jump labels in an IGA
> disassembly. */
> > +
> > +static const char *
> > +intelgt_disasm_sym_cb (int addr, void *ctx)
> > +{
> > + disassemble_info *info = (disassemble_info *) ctx;
> > + symbol *sym = find_pc_function (addr + (uintptr_t) info-
> >private_data);
> > + return sym ? sym->linkage_name () : nullptr;
> > +}
> > +#endif /* defined (HAVE_LIBIGA64) */
> > +
> > /* Print one instruction from MEMADDR on INFO->STREAM. */
> >
> > static int
> > intelgt_print_insn (bfd_vma memaddr, struct disassemble_info *info)
> > {
> > - /* Disassembler is to be added in a later patch. */
> > +#if !defined (HAVE_LIBIGA64)
> > + gdb_printf (_("\nDisassemble feature not available: libiga64 "
> > + "is missing.\n"));
> > return -1;
> > +#else
> > + std::unique_ptr<bfd_byte[]> insn (new
> bfd_byte[intelgt::MAX_INST_LENGTH]);
>
> Can this be statically allocated?
>
> bfd_byte insn[intelgt::MAX_INST_LENGTH];
Yes.
> > +
> > + int status = (*info->read_memory_func) (memaddr, insn.get (),
> > + intelgt::COMPACT_INST_LENGTH, info);
> > + if (status != 0)
> > + {
> > + /* Aborts disassembling with a memory_error exception. */
> > + (*info->memory_error_func) (status, memaddr, info);
> > + return -1;
> > + }
> > +
> > + uint32_t device_id = get_device_id (current_inferior ());
> > + gdb::array_view<bfd_byte> insn_view
> > + = gdb::make_array_view (insn.get (),
> intelgt::COMPACT_INST_LENGTH);
> > + unsigned int length = intelgt::inst_length (insn_view, device_id);
> > +
> > + if (length == intelgt::MAX_INST_LENGTH)
> > + {
> > + status = (*info->read_memory_func) (memaddr, insn.get (),
> > + intelgt::MAX_INST_LENGTH, info);
> > + if (status != 0)
> > + {
> > + /* Aborts disassembling with a memory_error exception. */
> > + (*info->memory_error_func) (status, memaddr, info);
> > + return -1;
> > + }
> > + }
> > +
> > + char *dbuf;
> > + iga_disassemble_options_t dopts = IGA_DISASSEMBLE_OPTIONS_INIT ();
> > + gdb_disassemble_info *di
> > + = static_cast<gdb_disassemble_info *>(info->application_data);
>
> Space before paren.
Fixed.
> > @@ -771,6 +842,46 @@ intelgt_gdbarch_init (gdbarch_info info,
> gdbarch_list *arches)
> > intelgt_gdbarch_tdep *data
> > = gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch);
> >
> > +#if defined (HAVE_LIBIGA64)
> > + iga_gen_t iga_version = IGA_GEN_INVALID;
> > +
> > + if (tdesc != nullptr)
> > + {
> > + const tdesc_device *device_info = tdesc_device_info (tdesc);
> > + if (!(device_info->vendor_id.has_value ()
> > + && device_info->target_id.has_value ()))
> > + {
> > + warning (_("Device vendor id and target id not found."));
>
> Perhaps say "... not found in target description."? Trying to think
> about a clueless user seeing this message without much context.
Updating with "... not found in intelgt target description."
> > + 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;
>
> I don't think these two gdbarch_free are needed, that is managed by
> gdbarch_u.
Thanks. These are leftovers from the time when gdbarch_u was not used.
Removed.
> > + }
> > + else
>
> I would remove this "else" (just de-indent the code below).
Done.
> > + {
> > + iga_version = (iga_gen_t) intelgt::get_xe_version (device_id);
> > + if (iga_version == IGA_GEN_INVALID)
> > + warning (_("Intel GT device id is unrecognized: ID 0x%04x"),
> > + device_id);
> > + }
> > + }
> > +
> > + /* Take the best guess in case IGA_VERSION is still invalid. */
> > + if (iga_version == IGA_GEN_INVALID)
> > + iga_version = IGA_XE_HPC;
> > +
> > + const iga_context_options_t options = IGA_CONTEXT_OPTIONS_INIT
> (iga_version);
> > + iga_context_create (&options, &data->iga_ctx);
>
> Should probably check the return value of iga_context_create.
Done.
>
> Simon
Thank you.
-Baris
Intel Deutschland GmbH
Registered Address: Dornacher Straße 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht München HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread
* [PATCH v3 09/44] gdb, gdbserver, ze: in-memory libraries
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (7 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 08/44] gdb, intelgt: add disassemble feature " Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-12-12 4:13 ` Simon Marchi
2025-08-01 9:37 ` [PATCH v3 10/44] gdb, gdbserver, rsp, ze: acknowledge libraries Tankut Baris Aktemur
` (37 subsequent siblings)
46 siblings, 1 reply; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
From: Markus Metzger <markus.t.metzger@intel.com>
For Intel GPU devices, device libraries live in the host memory and are
loaded onto the device from there.
Add support for reporting such in-memory shared libraries via
qXfer:libraries:read
and have GDB read them from target memory.
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
gdb/NEWS | 6 ++
gdb/doc/gdb.texinfo | 43 +++++++++----
gdb/features/library-list.dtd | 8 ++-
gdb/remote.c | 3 +
gdb/solib-target.c | 88 +++++++++++++++++++++++++--
gdb/solib-target.h | 2 +
gdb/solib.c | 31 +++++++++-
gdb/solib.h | 12 +++-
gdbserver/dll.cc | 95 +++++++++++++++++++++++------
gdbserver/dll.h | 34 ++++++++++-
gdbserver/server.cc | 137 ++++++++++++++++++++++++++++++++++++++++--
gdbserver/server.h | 3 +
12 files changed, 415 insertions(+), 47 deletions(-)
diff --git a/gdb/NEWS b/gdb/NEWS
index 5736565d4758a31ad7902905d280c2f68f7a8b08..36944e1a0a408aab3200bcd32c8ee462bb06428e 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -196,6 +196,12 @@ vFile:lstat
vFile:stat but if the filename is a symbolic link, return
information about the link itself, the file the link refers to.
+qXfer:libraries:read's response
+
+ The qXfer:libraries:read query supports reporting in-memory libraries. GDB
+ indicates support by supplying qXfer:libraries:read:in-memory-library+ in the
+ qSupported packet.
+
* Changed remote packets
qXfer:threads:read
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index a4c98d18e20182765f8cf0546447c6777cdd5e2d..628ee49b703c23888f518af584128111c9473ef9 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -24882,6 +24882,9 @@ future connections is shown. The available settings are:
@tab @code{no resumed thread left stop reply}
@tab Tracking thread lifetime.
+@item @code{qXfer:libraries:read:in-memory-library}
+@tab @code{in-memory-library library elements}
+@tab Support for in-memory libraries.
@end multitable
@cindex packet size, remote, configuring
@@ -48295,9 +48298,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
@@ -48309,6 +48313,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:
@@ -48320,6 +48328,16 @@ offset, looks like this:
</library-list>
@end smallexample
+A corresponding memory map for an in-memory library looks like this:
+
+@smallexample
+<library-list>
+ <in-memory-library begin="0xa000000" end="0xa001000">
+ <segment address="0x10000000"/>
+ </in-memory-library>
+</library-list>
+@end smallexample
+
Another simple memory map, with one loaded library with three
allocated sections (.text, .data, .bss), looks like this:
@@ -48337,14 +48355,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 66945cbe97c13cfac5a4d00e9a752ccd8419252f..baa01485af950c58e9e242229365501c043e2fe1 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 6208a90f94a70bf25b0e16cfc4dcce1e01f28828..3d3acd19a53c3665055f8e27cc8669916083f562 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -5975,6 +5975,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 770028d9903953cb761c2901aae5a1f247aaa34b..5d9bc8f53204eb7e629f3807bcb170c6ace357aa 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
- solib; it is only here during XML parsing. */
+ solib; 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,31 @@ target_solib_ops::current_sos () const
for (lm_info_target_up &info : library_list)
{
auto &new_solib = sos.emplace_back (*this);
+ switch (info->location)
+ {
+ case lm_on_disk:
+ /* We don't need a copy of the name in INFO anymore. */
+ new_solib.name = std::move (info->name);
+ new_solib.original_name = new_solib.name;
+ break;
+
+ case lm_in_memory:
+ if (info->end <= info->begin)
+ warning (_("bad in-memory-library location: begin=%s, end=%s"),
+ core_addr_to_string_nz (info->begin),
+ core_addr_to_string_nz (info->end));
+ else
+ {
+ new_solib.original_name = std::string ("in-memory-")
+ + core_addr_to_string_nz (info->begin)
+ + "-"
+ + core_addr_to_string_nz (info->end);
- /* We don't need a copy of the name in INFO anymore. */
- new_solib.name = std::move (info->name);
- new_solib.original_name = new_solib.name;
+ new_solib.begin = info->begin;
+ new_solib.end = info->end;
+ }
+ break;
+ }
new_solib.lm_info = std::move (info);
}
@@ -386,6 +456,14 @@ target_solib_ops::in_dynsym_resolve_code (CORE_ADDR pc) const
return in_plt_section (pc);
}
+gdb_bfd_ref_ptr
+target_solib_ops::bfd_open_from_target_memory (CORE_ADDR addr,
+ CORE_ADDR size,
+ const char *target) const
+{
+ return gdb_bfd_open_from_target_memory (addr, size, target);
+}
+
/* See solib-target.h. */
solib_ops_up
diff --git a/gdb/solib-target.h b/gdb/solib-target.h
index 89ae2bc9b9e155b9cc31b7410a814e2340ab867a..ecd67658c71cb854f012c4311ba79bb40a2ca1b1 100644
--- a/gdb/solib-target.h
+++ b/gdb/solib-target.h
@@ -29,6 +29,8 @@ struct target_solib_ops : solib_ops
void relocate_section_addresses (solib &so, target_section *) const override;
owning_intrusive_list<solib> current_sos () const override;
bool in_dynsym_resolve_code (CORE_ADDR pc) const override;
+ gdb_bfd_ref_ptr bfd_open_from_target_memory
+ (CORE_ADDR addr, CORE_ADDR size, const char *target) const override;
};
/* Return a new solib_ops for systems fetching solibs from the target. */
diff --git a/gdb/solib.c b/gdb/solib.c
index 3ec2032f01289011ba1bc3a651b076364f9438f1..f2b26d4e9f9b56b0d174e5223669bd392c3114de 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -473,6 +473,14 @@ solib_ops::bfd_open (const char *pathname) const
return solib_bfd_open (pathname);
}
+gdb_bfd_ref_ptr
+solib_ops::bfd_open_from_target_memory (CORE_ADDR addr,
+ CORE_ADDR size,
+ const char *target) const
+{
+ error (_("Target does not support in-memory shared libraries."));
+}
+
/* Given a pointer to one of the shared objects in our list of mapped
objects, use the recorded name to open a bfd descriptor for the
object, build a section table, relocate all the section addresses
@@ -488,8 +496,25 @@ solib_ops::bfd_open (const char *pathname) const
static int
solib_map_sections (solib &so)
{
- gdb::unique_xmalloc_ptr<char> filename (tilde_expand (so.name.c_str ()));
- gdb_bfd_ref_ptr abfd (so.ops ().bfd_open (filename.get ()));
+ gdb_bfd_ref_ptr abfd;
+ if (!so.name.empty ())
+ {
+ gdb::unique_xmalloc_ptr<char> filename (tilde_expand (so.name.c_str ()));
+ abfd = so.ops ().bfd_open (filename.get ());
+ }
+ else if (so.begin != 0 && so.end != 0)
+ {
+ 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 = so.ops ().bfd_open_from_target_memory (so.begin,
+ so.end - so.begin,
+ gnutarget);
+ }
+ else
+ internal_error (_("bad so_list"));
/* If we have a core target then the core target might have some helpful
information (i.e. build-ids) about the shared libraries we are trying
@@ -536,7 +561,7 @@ solib_map_sections (solib &so)
{
warning (_("Build-id of %ps does not match core file."),
styled_string (file_name_style.style (),
- filename.get ()));
+ so.name.c_str ()));
abfd = nullptr;
}
}
diff --git a/gdb/solib.h b/gdb/solib.h
index b9465e103bdd89c83ba575d7551ce84e5e1c7a36..32101d27cc5acae22939eb6a9f72239aaf4812a4 100644
--- a/gdb/solib.h
+++ b/gdb/solib.h
@@ -94,9 +94,14 @@ struct solib : intrusive_list_node<solib>
map we've already loaded. */
std::string original_name;
- /* Shared object file name, expanded to something GDB can open. */
+ /* Shared object file name, expanded to something GDB can open.
+ This is an empty string for in-memory shared objects. */
std::string name;
+ /* The address range of an in-memory shared object. Both BEGIN and END
+ are zero for on-disk shared objects. */
+ CORE_ADDR begin = 0, end = 0;
+
/* The following fields of the structure are built from
information gathered from the shared object file itself, and
are set when we actually add it to our symbol tables.
@@ -259,6 +264,11 @@ struct solib_ops
The supports_namespaces method must return true for this to be called. */
virtual std::vector<const solib *> get_solibs_in_ns (int ns) const
{ gdb_assert_not_reached ("namespaces not supported"); }
+
+ /* Open an in-memory shared library at ADDR of at most SIZE bytes.
+ The TARGET string is used to identify the target. */
+ virtual gdb_bfd_ref_ptr bfd_open_from_target_memory
+ (CORE_ADDR addr, CORE_ADDR size, const char *target) const;
};
/* A unique pointer to an solib_ops. */
diff --git a/gdbserver/dll.cc b/gdbserver/dll.cc
index 5ce8b2b45d508bab69dd6c57cce6d4f60911d065..c8e7c03012a6fd1650a7f7a4827f79b3735d624b 100644
--- a/gdbserver/dll.cc
+++ b/gdbserver/dll.cc
@@ -40,34 +40,44 @@ loaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
proc->dlls_changed = true;
}
-/* Record that the DLL with NAME and BASE_ADDR has been unloaded
- from the current process. */
+/* Record a newly loaded in-memory DLL at BASE_ADDR for PROC. */
void
-unloaded_dll (const char *name, CORE_ADDR base_addr)
+loaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
+ CORE_ADDR base_addr)
{
- unloaded_dll (current_process (), name, base_addr);
+ gdb_assert (proc != nullptr);
+
+ /* We do not support duplicate in-memory libraries. */
+ std::list<dll_info> &dlls = proc->all_dlls;
+ std::list<dll_info>::iterator it
+ = std::find_if (dlls.begin (), dlls.end (),
+ [begin, end] (const dll_info &dll)
+ {
+ return ((dll.begin == begin) && (dll.end == end));
+ });
+
+ if (it != dlls.end ())
+ error (_("Duplicate in-memory library; begin: %s, end: %s, base: %s."),
+ paddress (begin), paddress (end), paddress (base_addr));
+
+ proc->all_dlls.emplace_back (begin, end, base_addr);
+ proc->dlls_changed = true;
}
/* Record that the DLL with NAME and BASE_ADDR has been unloaded
- from PROC. */
+ from the current process. */
void
-unloaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
+unloaded_dll (const char *name, CORE_ADDR base_addr)
{
- gdb_assert (proc != nullptr);
- auto pred = [&] (const dll_info &dll)
- {
- if (base_addr != UNSPECIFIED_CORE_ADDR
- && base_addr == dll.base_addr)
- return true;
-
- if (name != NULL && dll.name == name)
- return true;
-
- return false;
- };
+ unloaded_dll (current_process (), name, base_addr);
+}
+static void
+unload_dll_if (process_info *proc,
+ std::function<bool (const dll_info &)> pred)
+{
auto iter = std::find_if (proc->all_dlls.begin (), proc->all_dlls.end (),
pred);
@@ -89,3 +99,52 @@ unloaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
proc->dlls_changed = true;
}
}
+
+/* Record that the DLL with NAME and BASE_ADDR has been unloaded
+ from PROC. */
+
+void
+unloaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
+{
+ unload_dll_if (proc, [&] (const dll_info &dll)
+ {
+ if (dll.location != dll_info::on_disk)
+ return false;
+
+ if (base_addr != UNSPECIFIED_CORE_ADDR
+ && base_addr == dll.base_addr)
+ return true;
+
+ if (name != NULL && dll.name == name)
+ return true;
+
+ return false;
+ });
+}
+
+/* Record that the in-memory DLL from BEGIN to END loaded at BASE_ADDR has been
+ unloaded. */
+
+void
+unloaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
+ CORE_ADDR base_addr)
+{
+ unload_dll_if (proc, [&] (const dll_info &dll)
+ {
+ if (dll.location != dll_info::in_memory)
+ return false;
+
+ if (base_addr != UNSPECIFIED_CORE_ADDR
+ && base_addr == dll.base_addr)
+ return true;
+
+ /* We do not require the end address to be specified - we don't
+ support partially unloaded libraries, anyway. */
+ if ((begin == dll.begin)
+ && (end == UNSPECIFIED_CORE_ADDR
+ || end == dll.end))
+ return true;
+
+ return false;
+ });
+}
diff --git a/gdbserver/dll.h b/gdbserver/dll.h
index b5d9c3957f0ab6851083aee342f90b74b4dd10d9..a3af86dd6122ff8b8c47a335890f54a906f6d5be 100644
--- a/gdbserver/dll.h
+++ b/gdbserver/dll.h
@@ -18,25 +18,57 @@
#ifndef GDBSERVER_DLL_H
#define GDBSERVER_DLL_H
+#include "gdbsupport/gdb_unlinker.h"
#include <list>
struct process_info;
struct dll_info
{
+ enum location_t
+ {
+ on_disk,
+ in_memory
+ };
+
dll_info (const std::string &name_, CORE_ADDR base_addr_)
- : name (name_), base_addr (base_addr_)
+ : location (on_disk), name (name_), base_addr (base_addr_)
+ {}
+
+ dll_info (CORE_ADDR begin_, CORE_ADDR end_, CORE_ADDR base_addr_)
+ : location (in_memory), begin (begin_), end (end_), base_addr (base_addr_)
{}
+ /* Where the library bits are stored. */
+ location_t location;
+
+ /* The name of a file on disk containing the library.
+
+ This is only valid if LOCATION == ON_DISK. */
std::string name;
+
+ /* An optional unlinker in case this is a temporary file. */
+ std::optional<gdb::unlinker> unlinker;
+
+ /* The address range in memory containing the library.
+
+ This is only valid if LOCATION == IN_MEMORY. */
+ CORE_ADDR begin;
+ CORE_ADDR end;
+
+ /* The base address at which the library is loaded. */
CORE_ADDR base_addr;
};
extern void loaded_dll (const char *name, CORE_ADDR base_addr);
extern void loaded_dll (process_info *proc, const char *name,
CORE_ADDR base_addr);
+extern void loaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
+ CORE_ADDR base_addr);
extern void unloaded_dll (const char *name, CORE_ADDR base_addr);
extern void unloaded_dll (process_info *proc, const char *name,
CORE_ADDR base_addr);
+extern void unloaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
+ CORE_ADDR base_addr);
#endif /* GDBSERVER_DLL_H */
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index ce84c7f75d98999117752465fd129226b0f443e9..aa695f2f2f80686ea6dc2ae3ae1bfd7c081067bf 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -1885,6 +1885,128 @@ handle_qxfer_features (const char *annex,
return len;
}
+/* Turn an in-memory library DLL into an on-disk library using a temporary
+ file. */
+
+static void
+dll_to_tmpfile (dll_info &dll)
+{
+ gdb_assert (dll.location == dll_info::in_memory);
+
+ 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));
+
+ std::vector<unsigned char> buffer (dll.end - dll.begin);
+ int errcode = gdb_read_memory (dll.begin, buffer.data (), buffer.size ());
+ if (errcode != buffer.size ())
+ 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 = get_standard_temp_dir ()
+ + "/gdb-in-memory-"
+ + core_addr_to_string_nz (dll.begin)
+ + "-"
+ + core_addr_to_string_nz (dll.end);
+
+ gdb::char_vector tmpname = make_temp_filename (name);
+
+ scoped_fd fd { gdb_mkostemp_cloexec (tmpname.data (), O_BINARY) };
+ if (fd.get () == -1)
+ error (_("failed to create temporary file %s: %s"), tmpname.data (),
+ safe_strerror (errno));
+
+ gdb::unlinker unlinker (tmpname.data ());
+ size_t size = buffer.size ();
+ unsigned char *head = buffer.data ();
+ do
+ {
+ ssize_t written = write (fd.get (), head, size);
+ if (written <= 0)
+ error (_("failed to write into %s"), name.c_str ());
+
+ head += written;
+ size -= written;
+ }
+ while (size > 0);
+
+ dll.location = dll_info::on_disk;
+ dll.name = tmpname.data ();
+ dll.unlinker.emplace (dll.name.c_str ());
+
+ unlinker.keep ();
+}
+
+/* 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. */
+ try
+ {
+ dll_to_tmpfile (dll);
+ }
+ catch (const gdb_exception &ex)
+ {
+ warning ("%s", ex.what ());
+ return std::string ();
+ }
+
+ [[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));
+ }
+
+ gdb_assert_not_reached ("unknown dll location: %x", dll.location);
+}
+
+/* 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
@@ -1898,13 +2020,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";
@@ -2742,6 +2864,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. */
@@ -2772,6 +2896,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 5609584f716488fca599a8d30dfa0f11b50042c8..ebb5980f70dd778abfaade1086b4b89a529b4298 100644
--- a/gdbserver/server.h
+++ b/gdbserver/server.h
@@ -196,6 +196,9 @@ 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;
+
+ /* True if qXfer:libraries:read supports in-memory-library. */
+ bool in_memory_library_supported = false;
};
client_state &get_client_state ();
--
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] 92+ messages in thread* Re: [PATCH v3 09/44] gdb, gdbserver, ze: in-memory libraries
2025-08-01 9:37 ` [PATCH v3 09/44] gdb, gdbserver, ze: in-memory libraries Tankut Baris Aktemur
@ 2025-12-12 4:13 ` Simon Marchi
2025-12-12 11:20 ` Metzger, Markus T
0 siblings, 1 reply; 92+ messages in thread
From: Simon Marchi @ 2025-12-12 4:13 UTC (permalink / raw)
To: Tankut Baris Aktemur, gdb-patches, Markus Metzger
On 2025-08-01 05:37, Tankut Baris Aktemur wrote:
> From: Markus Metzger <markus.t.metzger@intel.com>
>
> For Intel GPU devices, device libraries live in the host memory and are
> loaded onto the device from there.
>
> Add support for reporting such in-memory shared libraries via
>
> qXfer:libraries:read
>
> and have GDB read them from target memory.
>
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
From the ROCm point of view, it seems like this might be useful if we
ever do remote debugging, since we do have these in-memory "shared
libraries".
> @@ -48309,6 +48313,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:
>
Should there be an entry in the "The following values of gdbfeature
(for the packet sent by GDB) are defined:" section of the qSupported
doc? I'm a bit confused, because it seems like some values that GDB
sends along with qSupported are not listed there. Perhaps it's an
oversight and they should?
> diff --git a/gdb/features/library-list.dtd b/gdb/features/library-list.dtd
> index 66945cbe97c13cfac5a4d00e9a752ccd8419252f..baa01485af950c58e9e242229365501c043e2fe1 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">
Hmm, looking at the history of this file and other .dtd files, we added
attributes and elements in the past without bumping this version number.
I guess it's not needed because we do our own checking of what the other
side supports via the qSupported packet and adjust the behavior based on
that. If not useful, I think we could keep 1.0 here for simplicity.
> @@ -246,10 +295,31 @@ target_solib_ops::current_sos () const
> for (lm_info_target_up &info : library_list)
> {
> auto &new_solib = sos.emplace_back (*this);
> + switch (info->location)
> + {
> + case lm_on_disk:
> + /* We don't need a copy of the name in INFO anymore. */
> + new_solib.name = std::move (info->name);
> + new_solib.original_name = new_solib.name;
> + break;
> +
> + case lm_in_memory:
> + if (info->end <= info->begin)
> + warning (_("bad in-memory-library location: begin=%s, end=%s"),
> + core_addr_to_string_nz (info->begin),
> + core_addr_to_string_nz (info->end));
> + else
> + {
> + new_solib.original_name = std::string ("in-memory-")
> + + core_addr_to_string_nz (info->begin)
> + + "-"
> + + core_addr_to_string_nz (info->end);
Align with parenthesis:
new_solib.original_name = (std::string ("in-memory-")
+ core_addr_to_string_nz (info->begin)
+ "-"
+ core_addr_to_string_nz (info->end));
Just a note regarding ROCm. We have a particular syntax for in-memory
objects, like this:
memory://57663#offset=0x55555562b040&size=62488
It is documented here:
https://llvm.org/docs/AMDGPUUsage.html#loaded-code-object-path-uniform-resource-identifier-uri
Currently, solib-rocm.c generates solibs with that kind of name for
in-memory objects. If we add proper support for solibs in memory, like
this patch does, we'll probably want to change solib-rocm to use that.
However, we'll want to make sure that "info shared" still shows the URIs
in the format shown above, at least for ROCm. We can cross this bridge
when we get there, but we'll need to find a way.
> - /* We don't need a copy of the name in INFO anymore. */
> - new_solib.name = std::move (info->name);
> - new_solib.original_name = new_solib.name;
> + new_solib.begin = info->begin;
> + new_solib.end = info->end;
I think I'd like to see two solib constructors, one for on-disk and one
for memory. That would make it clear what information is required in
each case.
> @@ -386,6 +456,14 @@ target_solib_ops::in_dynsym_resolve_code (CORE_ADDR pc) const
> return in_plt_section (pc);
> }
>
> +gdb_bfd_ref_ptr
> +target_solib_ops::bfd_open_from_target_memory (CORE_ADDR addr,
> + CORE_ADDR size,
> + const char *target) const
> +{
> + return gdb_bfd_open_from_target_memory (addr, size, target);
> +}
Would it make sense for this to be the default implementation
(solib_ops::bfd_open_from_target_memory)?
> @@ -488,8 +496,25 @@ solib_ops::bfd_open (const char *pathname) const
> static int
> solib_map_sections (solib &so)
> {
> - gdb::unique_xmalloc_ptr<char> filename (tilde_expand (so.name.c_str ()));
> - gdb_bfd_ref_ptr abfd (so.ops ().bfd_open (filename.get ()));
> + gdb_bfd_ref_ptr abfd;
> + if (!so.name.empty ())
> + {
> + gdb::unique_xmalloc_ptr<char> filename (tilde_expand (so.name.c_str ()));
> + abfd = so.ops ().bfd_open (filename.get ());
> + }
> + else if (so.begin != 0 && so.end != 0)
> + {
> + 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));
Sounds like we should never end up with an solib like this, I don't
think we need to handle this case here.
> +
> + abfd = so.ops ().bfd_open_from_target_memory (so.begin,
> + so.end - so.begin,
> + gnutarget);
> + }
> + else
> + internal_error (_("bad so_list"));
This error message could be a bit prettier.
> +/* 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. */
Curious, do you actually need this fallback? If not, could GDBserver
just not report these in-memory libraries if GDB is too old? If we can
avoid the complexity...
> @@ -2772,6 +2896,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+");
So, this makes gdbserver reply "I support in memory libraries". Is it
really needed? It seems to me like it's only important for GDB to tell
gdbserver, not the other way around.
Simon
^ permalink raw reply [flat|nested] 92+ messages in thread* RE: [PATCH v3 09/44] gdb, gdbserver, ze: in-memory libraries
2025-12-12 4:13 ` Simon Marchi
@ 2025-12-12 11:20 ` Metzger, Markus T
2025-12-12 19:34 ` Simon Marchi
0 siblings, 1 reply; 92+ messages in thread
From: Metzger, Markus T @ 2025-12-12 11:20 UTC (permalink / raw)
To: Simon Marchi; +Cc: Aktemur, Tankut Baris, gdb-patches, Thiago Jung Bauermann
Hello Simon,
Thanks for your review.
>> diff --git a/gdb/features/library-list.dtd b/gdb/features/library-list.dtd
>> index
>66945cbe97c13cfac5a4d00e9a752ccd8419252f..baa01485af950c58e9e242229
>365501c043e2fe1 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">
>
>Hmm, looking at the history of this file and other .dtd files, we added
>attributes and elements in the past without bumping this version number.
>I guess it's not needed because we do our own checking of what the other
>side supports via the qSupported packet and adjust the behavior based on
>that. If not useful, I think we could keep 1.0 here for simplicity.
This has been discussed with Thiago, as well in
https://sourceware.org/pipermail/gdb-patches/2025-July/219545.html
Thiago thought that the version bump was necessary, but suggested bumping
it once for the entire series and not for each patch that makes changes to it.
I'm fine, either way, but I wonder why we major.minor version if we then ignore it.
> new_solib.original_name = (std::string ("in-memory-")
> + core_addr_to_string_nz (info-
>>begin)
> + "-"
> + core_addr_to_string_nz (info-
>>end));
>
>Just a note regarding ROCm. We have a particular syntax for in-memory
>objects, like this:
>
> memory://57663#offset=0x55555562b040&size=62488
>
>It is documented here:
>
>https://llvm.org/docs/AMDGPUUsage.html#loaded-code-object-path-uniform-
>resource-identifier-uri
>
>Currently, solib-rocm.c generates solibs with that kind of name for
>in-memory objects. If we add proper support for solibs in memory, like
>this patch does, we'll probably want to change solib-rocm to use that.
>However, we'll want to make sure that "info shared" still shows the URIs
>in the format shown above, at least for ROCm. We can cross this bridge
>when we get there, but we'll need to find a way.
The name that gets printed by 'info shared' is defined by the in-memory bfd.
I don't know where this name is used. Looks like it is OK to not provide a name.
>> + 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));
>
>Sounds like we should never end up with an solib like this, I don't
>think we need to handle this case here.
We're subtracting so.begin from so.end. If you don't like the error, we
should assert this. The only place that currently adds such solibs ignores
those with a warning, so asserting it should be fine.
>> +/* 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. */
>
>Curious, do you actually need this fallback? If not, could GDBserver
>just not report these in-memory libraries if GDB is too old? If we can
>avoid the complexity...
Since support will be there from the beginning for our target, we don't
actually need the fall-back. A GDB that doesn't support this also wouldn't
be able to support the IntelGT architecture.
I'm fine to drop it.
>> @@ -2772,6 +2896,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+");
>
>So, this makes gdbserver reply "I support in memory libraries". Is it
>really needed? It seems to me like it's only important for GDB to tell
>gdbserver, not the other way around.
Right, GDB needs to opt into this new response.
I thought it was common practice to announce features in gdbserver.
If we only do this for opt-ins, I can drop this.
Regards,
Markus.
Intel Deutschland GmbH
Registered Address: Dornacher Straße 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht München HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread* Re: [PATCH v3 09/44] gdb, gdbserver, ze: in-memory libraries
2025-12-12 11:20 ` Metzger, Markus T
@ 2025-12-12 19:34 ` Simon Marchi
2025-12-15 13:07 ` Metzger, Markus T
0 siblings, 1 reply; 92+ messages in thread
From: Simon Marchi @ 2025-12-12 19:34 UTC (permalink / raw)
To: Metzger, Markus T
Cc: Aktemur, Tankut Baris, gdb-patches, Thiago Jung Bauermann
On 12/12/25 6:20 AM, Metzger, Markus T wrote:
> Hello Simon,
>
> Thanks for your review.
>
>>> diff --git a/gdb/features/library-list.dtd b/gdb/features/library-list.dtd
>>> index
>> 66945cbe97c13cfac5a4d00e9a752ccd8419252f..baa01485af950c58e9e242229
>> 365501c043e2fe1 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">
>>
>> Hmm, looking at the history of this file and other .dtd files, we added
>> attributes and elements in the past without bumping this version number.
>> I guess it's not needed because we do our own checking of what the other
>> side supports via the qSupported packet and adjust the behavior based on
>> that. If not useful, I think we could keep 1.0 here for simplicity.
>
> This has been discussed with Thiago, as well in
> https://sourceware.org/pipermail/gdb-patches/2025-July/219545.html
>
> Thiago thought that the version bump was necessary, but suggested bumping
> it once for the entire series and not for each patch that makes changes to it.
>
> I'm fine, either way, but I wonder why we major.minor version if we then ignore it.
As far as I know, the GDB client doesn't even check these version
numbers, so I am not sure what bumping the version number would help
with. It won't prevent an older GDB from trying to read a newer
backwards-incompatible version.
I see two possibilities:
1. Adding a new attribute or element is considered
backwards-incompatible, in which case this version number should have
been bumped a few times already. But they haven't so... not sure
it's worth starting now.
2. Adding a new attribute or element is considered backwards-compatible
(an older client would just ignore them), so it's not necessary to
bump the version.
Would it work to just make gdbserver return these in-memory-library
elements, without even a qSupported flag? Would older GDBs just ignore
them?
>> new_solib.original_name = (std::string ("in-memory-")
>> + core_addr_to_string_nz (info-
>>> begin)
>> + "-"
>> + core_addr_to_string_nz (info-
>>> end));
>>
>> Just a note regarding ROCm. We have a particular syntax for in-memory
>> objects, like this:
>>
>> memory://57663#offset=0x55555562b040&size=62488
>>
>> It is documented here:
>>
>> https://llvm.org/docs/AMDGPUUsage.html#loaded-code-object-path-uniform-
>> resource-identifier-uri
>>
>> Currently, solib-rocm.c generates solibs with that kind of name for
>> in-memory objects. If we add proper support for solibs in memory, like
>> this patch does, we'll probably want to change solib-rocm to use that.
>> However, we'll want to make sure that "info shared" still shows the URIs
>> in the format shown above, at least for ROCm. We can cross this bridge
>> when we get there, but we'll need to find a way.
>
> The name that gets printed by 'info shared' is defined by the in-memory bfd.
>
> I don't know where this name is used. Looks like it is OK to not provide a name.
Ok, so "info shared" shows solib->name. For in-memory solibs, you don't
set solib->name? Does this mean that no name is shown in "info shared"
for these libraries?
As of today, even with this change, solib-rocm.c could set solib->name
to whatever it wants. If we ever want to support remote debugging with
ROCm, we would want to find a way to get our `memory://...` string as
the name.
Do you think that the remote side could provide a name inside an
<in-memory-library> element? A ROCm would put that `memory://...`
string. But in general, it would be a place to put a user-friendly name
that will end up in "info shared".
>
>>> + 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));
>>
>> Sounds like we should never end up with an solib like this, I don't
>> think we need to handle this case here.
>
> We're subtracting so.begin from so.end. If you don't like the error, we
> should assert this. The only place that currently adds such solibs ignores
> those with a warning, so asserting it should be fine.
Yes I think an assert makes sense here (or in an eventual solib
constructor that takes these two addresses). Filtering for bad solib
data would always be done earlier than that.
>>> +/* 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. */
>>
>> Curious, do you actually need this fallback? If not, could GDBserver
>> just not report these in-memory libraries if GDB is too old? If we can
>> avoid the complexity...
>
> Since support will be there from the beginning for our target, we don't
> actually need the fall-back. A GDB that doesn't support this also wouldn't
> be able to support the IntelGT architecture.
>
> I'm fine to drop it.
That would be my preference, otherwise it's code that will never
actually be used but will add maintenance cost.
>>> @@ -2772,6 +2896,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+");
>>
>> So, this makes gdbserver reply "I support in memory libraries". Is it
>> really needed? It seems to me like it's only important for GDB to tell
>> gdbserver, not the other way around.
>
> Right, GDB needs to opt into this new response.
>
> I thought it was common practice to announce features in gdbserver.
> If we only do this for opt-ins, I can drop this.
If GDB needs to know what gdbserver supports, it makes sense for
gdbserver to announce it, but I think it's only the other way around
in this case.
But really, if we can just add the new <in-memory-library> element and
it is gracefully ignored by older GDBs, without the need for a feature
flag, that would be the simplest solution (and therefore my preference).
Simon
^ permalink raw reply [flat|nested] 92+ messages in thread* RE: [PATCH v3 09/44] gdb, gdbserver, ze: in-memory libraries
2025-12-12 19:34 ` Simon Marchi
@ 2025-12-15 13:07 ` Metzger, Markus T
2025-12-15 21:25 ` Simon Marchi
0 siblings, 1 reply; 92+ messages in thread
From: Metzger, Markus T @ 2025-12-15 13:07 UTC (permalink / raw)
To: Simon Marchi; +Cc: Aktemur, Tankut Baris, gdb-patches, Thiago Jung Bauermann
>>>> <!-- 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">
>>>
>>> Hmm, looking at the history of this file and other .dtd files, we added
>>> attributes and elements in the past without bumping this version number.
>>> I guess it's not needed because we do our own checking of what the other
>>> side supports via the qSupported packet and adjust the behavior based on
>>> that. If not useful, I think we could keep 1.0 here for simplicity.
>>
>> This has been discussed with Thiago, as well in
>> https://sourceware.org/pipermail/gdb-patches/2025-July/219545.html
>>
>> Thiago thought that the version bump was necessary, but suggested
>bumping
>> it once for the entire series and not for each patch that makes changes to it.
>>
>> I'm fine, either way, but I wonder why we major.minor version if we then
>ignore it.
>
>As far as I know, the GDB client doesn't even check these version
>numbers, so I am not sure what bumping the version number would help
>with. It won't prevent an older GDB from trying to read a newer
>backwards-incompatible version.
>
>I see two possibilities:
>
>1. Adding a new attribute or element is considered
> backwards-incompatible, in which case this version number should have
> been bumped a few times already. But they haven't so... not sure
> it's worth starting now.
>
>2. Adding a new attribute or element is considered backwards-compatible
> (an older client would just ignore them), so it's not necessary to
> bump the version.
>
>Would it work to just make gdbserver return these in-memory-library
>elements, without even a qSupported flag? Would older GDBs just ignore
>them?
GDB silently ignores unknown elements and unknown attributes.
I will drop most of this patch and just send the new in-memory-library
response without opt-in and without fallback.
>>> new_solib.original_name = (std::string ("in-memory-")
>>> + core_addr_to_string_nz (info-
>>>> begin)
>>> + "-"
>>> + core_addr_to_string_nz (info-
>>>> end));
>>>
>>> Just a note regarding ROCm. We have a particular syntax for in-memory
>>> objects, like this:
>>>
>>> memory://57663#offset=0x55555562b040&size=62488
>>>
>>> It is documented here:
>>>
>>> https://llvm.org/docs/AMDGPUUsage.html#loaded-code-object-path-
>uniform-
>>> resource-identifier-uri
>>>
>>> Currently, solib-rocm.c generates solibs with that kind of name for
>>> in-memory objects. If we add proper support for solibs in memory, like
>>> this patch does, we'll probably want to change solib-rocm to use that.
>>> However, we'll want to make sure that "info shared" still shows the URIs
>>> in the format shown above, at least for ROCm. We can cross this bridge
>>> when we get there, but we'll need to find a way.
>>
>> The name that gets printed by 'info shared' is defined by the in-memory bfd.
>>
>> I don't know where this name is used. Looks like it is OK to not provide a
>name.
>
>Ok, so "info shared" shows solib->name. For in-memory solibs, you don't
>set solib->name? Does this mean that no name is shown in "info shared"
>for these libraries?
No, 'info shared' prints the name given by
/* Constructor. BASE and SIZE define where the BFD can be found in
target memory. */
target_buffer (CORE_ADDR base, ULONGEST size)
: m_base (base),
m_size (size),
m_filename (xstrprintf ("<in-memory@%s-%s>",
core_addr_to_string_nz (m_base),
core_addr_to_string_nz (m_base + m_size)))
This is existing code. I'm no longer setting solib->name for in-memory libs.
>As of today, even with this change, solib-rocm.c could set solib->name
>to whatever it wants. If we ever want to support remote debugging with
>ROCm, we would want to find a way to get our `memory://...` string as
>the name.
>
>Do you think that the remote side could provide a name inside an
><in-memory-library> element? A ROCm would put that `memory://...`
>string. But in general, it would be a place to put a user-friendly name
>that will end up in "info shared".
We'd need to route that through. I have not looked into it. I'm quite happy
with the name GDB chooses. It makes it easy to copy&paste to a 'dump
memory' command.
>>>> + 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));
>>>
>>> Sounds like we should never end up with an solib like this, I don't
>>> think we need to handle this case here.
>>
>> We're subtracting so.begin from so.end. If you don't like the error, we
>> should assert this. The only place that currently adds such solibs ignores
>> those with a warning, so asserting it should be fine.
>
>Yes I think an assert makes sense here (or in an eventual solib
>constructor that takes these two addresses). Filtering for bad solib
>data would always be done earlier than that.
I'd leave the assert here. This is where we need that property asserted.
>>>> +/* 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. */
>>>
>>> Curious, do you actually need this fallback? If not, could GDBserver
>>> just not report these in-memory libraries if GDB is too old? If we can
>>> avoid the complexity...
>>
>> Since support will be there from the beginning for our target, we don't
>> actually need the fall-back. A GDB that doesn't support this also wouldn't
>> be able to support the IntelGT architecture.
>>
>> I'm fine to drop it.
>
>That would be my preference, otherwise it's code that will never
>actually be used but will add maintenance cost.
>
>>>> @@ -2772,6 +2896,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+");
>>>
>>> So, this makes gdbserver reply "I support in memory libraries". Is it
>>> really needed? It seems to me like it's only important for GDB to tell
>>> gdbserver, not the other way around.
>>
>> Right, GDB needs to opt into this new response.
>>
>> I thought it was common practice to announce features in gdbserver.
>> If we only do this for opt-ins, I can drop this.
>
>If GDB needs to know what gdbserver supports, it makes sense for
>gdbserver to announce it, but I think it's only the other way around
>in this case.
>
>But really, if we can just add the new <in-memory-library> element and
>it is gracefully ignored by older GDBs, without the need for a feature
>flag, that would be the simplest solution (and therefore my preference).
OK.
Regards,
Markus.
Intel Deutschland GmbH
Registered Address: Dornacher Straße 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht München HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread* Re: [PATCH v3 09/44] gdb, gdbserver, ze: in-memory libraries
2025-12-15 13:07 ` Metzger, Markus T
@ 2025-12-15 21:25 ` Simon Marchi
0 siblings, 0 replies; 92+ messages in thread
From: Simon Marchi @ 2025-12-15 21:25 UTC (permalink / raw)
To: Metzger, Markus T
Cc: Aktemur, Tankut Baris, gdb-patches, Thiago Jung Bauermann
On 12/15/25 8:07 AM, Metzger, Markus T wrote:
>> Would it work to just make gdbserver return these in-memory-library
>> elements, without even a qSupported flag? Would older GDBs just ignore
>> them?
>
> GDB silently ignores unknown elements and unknown attributes.
>
> I will drop most of this patch and just send the new in-memory-library
> response without opt-in and without fallback.
Ack, glad to know it works.
>> Ok, so "info shared" shows solib->name. For in-memory solibs, you don't
>> set solib->name? Does this mean that no name is shown in "info shared"
>> for these libraries?
>
> No, 'info shared' prints the name given by
>
> /* Constructor. BASE and SIZE define where the BFD can be found in
> target memory. */
> target_buffer (CORE_ADDR base, ULONGEST size)
> : m_base (base),
> m_size (size),
> m_filename (xstrprintf ("<in-memory@%s-%s>",
> core_addr_to_string_nz (m_base),
> core_addr_to_string_nz (m_base + m_size)))
>
> This is existing code. I'm no longer setting solib->name for in-memory libs.
Ok, I am a bit confused, because I see that we pass a "name" at the
constructor of struct solib, but then solib_map_sections does:
so.name = bfd_get_filename (so.abfd.get ());
I guess these is how target_buffer::m_filename ends up as the solib
name.
>> As of today, even with this change, solib-rocm.c could set solib->name
>> to whatever it wants. If we ever want to support remote debugging with
>> ROCm, we would want to find a way to get our `memory://...` string as
>> the name.
>>
>> Do you think that the remote side could provide a name inside an
>> <in-memory-library> element? A ROCm would put that `memory://...`
>> string. But in general, it would be a place to put a user-friendly name
>> that will end up in "info shared".
>
> We'd need to route that through. I have not looked into it. I'm quite happy
> with the name GDB chooses. It makes it easy to copy&paste to a 'dump
> memory' command.
For ROCm, I think we'd really want to keep that syntax, because it's
used and understood by other tools in the ecosystem. As long as there
is a backwards-compatible way to implement it in the future, I am happy.
>>>>> + 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));
>>>>
>>>> Sounds like we should never end up with an solib like this, I don't
>>>> think we need to handle this case here.
>>>
>>> We're subtracting so.begin from so.end. If you don't like the error, we
>>> should assert this. The only place that currently adds such solibs ignores
>>> those with a warning, so asserting it should be fine.
>>
>> Yes I think an assert makes sense here (or in an eventual solib
>> constructor that takes these two addresses). Filtering for bad solib
>> data would always be done earlier than that.
>
> I'd leave the assert here. This is where we need that property asserted.
Assert yes, not error().
Simon
^ permalink raw reply [flat|nested] 92+ messages in thread
* [PATCH v3 10/44] gdb, gdbserver, rsp, ze: acknowledge libraries
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (8 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 09/44] gdb, gdbserver, ze: in-memory libraries Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-12-12 4:41 ` Simon Marchi
2025-08-01 9:37 ` [PATCH v3 11/44] gdb, solib, ze: update target_solib_ops::bfd_open_from_target_memory Tankut Baris Aktemur
` (36 subsequent siblings)
46 siblings, 1 reply; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
From: Markus Metzger <markus.t.metzger@intel.com>
On some accelerator devices, device shared libraries are loaded from a
host thread rather than from a device thread. The reporting entity may
not be the one that actually does the load.
Intel GPU devices, for example, that are based on Level-Zero, will report
shared library events via the device's debug interface. This is triggered
from a host thread calling the run-time interface for loading a device
shared library.
The Level-Zero run-time ensures that this host thread will not return
until the respective debug event has been acknowledged by the debugger.
This allows debuggers to set breakpoints before the new library is used.
Add a mechanism that allows gdbserver to request acknowledgement of newly
reported shared libraries and GDB to acknowledge requested libraries after
placing breakpoints.
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
gdb/NEWS | 7 +++
gdb/doc/gdb.texinfo | 76 ++++++++++++++++++++---
gdb/features/library-list.dtd | 24 +++----
gdb/remote.c | 114 ++++++++++++++++++++++++++++++++++
gdb/solib-target.c | 58 ++++++++++++++++-
gdb/solib-target.h | 1 +
gdb/solib.c | 14 ++++-
gdb/solib.h | 4 ++
gdb/target-delegates-gen.c | 50 +++++++++++++++
gdb/target.c | 16 +++++
gdb/target.h | 21 +++++++
gdbserver/dll.cc | 94 ++++++++++++++++++++++++++--
gdbserver/dll.h | 28 ++++++---
gdbserver/server.cc | 141 +++++++++++++++++++++++++++++++++++++++---
gdbserver/server.h | 4 ++
gdbserver/target.cc | 14 +++++
gdbserver/target.h | 20 ++++++
17 files changed, 644 insertions(+), 42 deletions(-)
diff --git a/gdb/NEWS b/gdb/NEWS
index 36944e1a0a408aab3200bcd32c8ee462bb06428e..e95550496c4190f7032e917329ce497844a79e67 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -202,6 +202,13 @@ qXfer:libraries:read's response
indicates support by supplying qXfer:libraries:read:in-memory-library+ in the
qSupported packet.
+vAck:library
+vAck:in-memory-library
+
+ Acknowledge libraries to gdbserver when requested. Libraries are acknowledged
+ after the initial processing by GDB such as loading symbols and placing
+ breakpoints.
+
* Changed remote packets
qXfer:threads:read
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 628ee49b703c23888f518af584128111c9473ef9..faa93177e0e9f0a6b83ec9dda1031276ae1c114d 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -43660,6 +43660,44 @@ for success (@pxref{Stop Reply Packets})
@cindex @samp{vStopped} packet
@xref{Notification Packets}.
+@item vAck:@var{type}:@var{arg}[,@var{arg}@dots{}][;@var{type}:@var{arg}[,@var{arg}@dots{}]]@dots{}
+@cindex @samp{vAck} packet
+@anchor{vAck packet}
+
+Acknowledge a @samp{;}-separated list of remote stub responses, each
+with a @samp{,}-separated list of arguments defined by its @var{type}.
+The following @var{type}s with their respective arguments are
+supported:
+
+@table @samp
+@item library:@var{name}
+Acknowledge the shared library that had been reported as
+@samp{<library name="@var{name}" ack="yes">} in the remote stub's
+@samp{qXfer:libraries:read} response. @value{GDBN} acknowledges
+libraries after initial processing like loading symbols and placing
+breakpoints. The file name is a hex-encoded string.
+
+@item in-memory-library:@var{begin},@var{end}
+Acknowledge the shared library that had been reported as
+@samp{<in-memory-library begin="@var{begin}" end="@var{end}" ack="yes">}
+in the remote stub's @samp{qXfer:libraries:read} response.
+@value{GDBN} acknowledges libraries after initial processing like
+loading symbols and placing breakpoints. Both arguments are unsigned
+integers.
+@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}
@anchor{x packet}
@cindex @samp{x} packet
@@ -45029,6 +45067,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
@@ -48303,6 +48349,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
@@ -48328,16 +48381,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"/>
</in-memory-library>
</library-list>
@end smallexample
+@value{GDBN} will acknowledge the library with a @samp{vAck;library}
+or, as in this case, a @samp{vAck;in-memory-library} packet.
+@xref{vAck packet}.
+
Another simple memory map, with one loaded library with three
allocated sections (.text, .data, .bss), looks like this:
@@ -48356,16 +48414,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.1">
<!ELEMENT library (segment*, section*)>
-<!ATTLIST library name CDATA #REQUIRED>
+<!ATTLIST library name CDATA #REQUIRED
+ ack (yes | no) 'no'>
<!ELEMENT in-memory-library (segment*, section*)>
-<!ATTLIST in-memory-library begin CDATA #REQUIRED
- end CDATA #REQUIRED>
+<!ATTLIST in-memory-library begin CDATA #REQUIRED
+ end CDATA #REQUIRED
+ ack (yes | no) 'no'>
<!ELEMENT segment EMPTY>
-<!ATTLIST segment address CDATA #REQUIRED>
+<!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 baa01485af950c58e9e242229365501c043e2fe1..0ee7eb364b97c61180fcaecca3637a081e32736b 100644
--- a/gdb/features/library-list.dtd
+++ b/gdb/features/library-list.dtd
@@ -5,18 +5,20 @@
notice and this notice are preserved. -->
<!-- library-list: Root element with versioning -->
-<!ELEMENT library-list (library | in-memory-library)*>
-<!ATTLIST library-list version CDATA #FIXED "1.1">
+<!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 library (segment*, section*)>
+<!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>
+<!ELEMENT in-memory-library (segment*, section*)>
+<!ATTLIST in-memory-library begin CDATA #REQUIRED
+ end CDATA #REQUIRED
+ ack (yes | no) 'no'>
-<!ELEMENT segment EMPTY>
-<!ATTLIST segment address CDATA #REQUIRED>
+<!ELEMENT segment EMPTY>
+<!ATTLIST segment address CDATA #REQUIRED>
-<!ELEMENT section EMPTY>
-<!ATTLIST section address CDATA #REQUIRED>
+<!ELEMENT section EMPTY>
+<!ATTLIST section address CDATA #REQUIRED>
diff --git a/gdb/remote.c b/gdb/remote.c
index 3d3acd19a53c3665055f8e27cc8669916083f562..a4daca885f77803374384adbe13524af3e62b12b 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -379,6 +379,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,
@@ -1157,6 +1163,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,
@@ -5864,6 +5873,10 @@ static const struct protocol_feature remote_protocol_features[] = {
{ "error-message", PACKET_ENABLE, remote_supported_packet,
PACKET_accept_error_message },
{ "binary-upload", PACKET_DISABLE, remote_supported_packet, PACKET_x },
+ { "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;
@@ -5978,6 +5991,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
@@ -15760,6 +15781,94 @@ remote_target::vcont_r_supported ()
&& get_remote_state ()->supports_vCont.r);
}
+/* Acknowledge processing of library NAME is complete. */
+
+void
+remote_target::ack_library (const char *name)
+{
+ if (m_features.packet_support (PACKET_vAck_library) == PACKET_DISABLE)
+ error (_("vAck:library packet disabled."));
+
+ remote_state *rs = get_remote_state ();
+ char *p = rs->buf.data ();
+ long size = get_remote_packet_size ();
+
+ int written = snprintf (p, size, "vAck:library:");
+ if (written == size)
+ error (_("Remote packet buffer too small for vAck:library packet."));
+
+ p += written;
+ size -= written;
+
+ int len = strnlen (name, size);
+ if (size <= (2 * len))
+ error (_("Library name too long for vAck:library packet."));
+
+ int converted = bin2hex ((const gdb_byte *) name, p, len);
+ if (converted < len)
+ error (_("Failed to encode library name for vAck:library packet."));
+
+ written = 2 * converted;
+ p += written;
+ size -= written;
+
+ gdb_assert (size > 0);
+ *p = 0;
+
+ 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 ());
+ }
+}
+
+/* Acknowledge processing of in-memory library BEGIN..END is complete. */
+
+void
+remote_target::ack_in_memory_library (CORE_ADDR begin, CORE_ADDR end)
+{
+ if (m_features.packet_support (PACKET_vAck_in_memory_library)
+ == PACKET_DISABLE)
+ error (_("vAck:in-memory-library packet disabled."));
+
+ remote_state *rs = get_remote_state ();
+ char *p = rs->buf.data ();
+ long size = get_remote_packet_size ();
+
+ int written = snprintf (p, size, "vAck:in-memory-library:%s,%s",
+ core_addr_to_string_nz (begin),
+ core_addr_to_string_nz (end));
+ if (written == size)
+ error (_("Remote packet buffer too small for vAck:in-memory-library "
+ "packet."));
+
+ 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
@@ -16525,6 +16634,11 @@ 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 5d9bc8f53204eb7e629f3807bcb170c6ace357aa..304237e9d60c0661e65ea6ae86c165bd02d125e5 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 }
};
@@ -464,6 +494,32 @@ target_solib_ops::bfd_open_from_target_memory (CORE_ADDR addr,
return gdb_bfd_open_from_target_memory (addr, size, target);
}
+void
+target_solib_ops::ack_library (solib &so) const
+{
+ 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.original_name.c_str ());
+ return;
+
+ case lm_in_memory:
+ target_ack_in_memory_library (lm->begin, lm->end);
+ return;
+ }
+
+ gdb_assert_not_reached ("bad solib location '%d' for %s.", lm->location,
+ so.original_name.c_str ());
+}
+
/* See solib-target.h. */
solib_ops_up
diff --git a/gdb/solib-target.h b/gdb/solib-target.h
index ecd67658c71cb854f012c4311ba79bb40a2ca1b1..a417052b541dbe75fee7f455ec907e2e1c843a05 100644
--- a/gdb/solib-target.h
+++ b/gdb/solib-target.h
@@ -31,6 +31,7 @@ struct target_solib_ops : solib_ops
bool in_dynsym_resolve_code (CORE_ADDR pc) const override;
gdb_bfd_ref_ptr bfd_open_from_target_memory
(CORE_ADDR addr, CORE_ADDR size, const char *target) const override;
+ void ack_library (solib &so) const override;
};
/* Return a new solib_ops for systems fetching solibs from the target. */
diff --git a/gdb/solib.c b/gdb/solib.c
index f2b26d4e9f9b56b0d174e5223669bd392c3114de..b7a552e80dc34ea5b69f55f4e25f5a390a57c2da 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -984,6 +984,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.name.c_str ()))
{
@@ -1006,14 +1007,23 @@ solib_add (const char *pattern, int from_tty, int readsyms)
styled_string (file_name_style.style (),
gdb.name.c_str ()));
}
- else if (solib_read_symbols (gdb, add_flags))
- loaded_any_symbols = true;
+ else
+ added_solibs.push_back (&gdb);
}
}
+ for (solib *gdb : added_solibs)
+ if (solib_read_symbols (*gdb, add_flags))
+ loaded_any_symbols = true;
+
if (loaded_any_symbols || !current_program_space->deleted_solibs.empty ())
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)
+ gdb->ops ().ack_library (*gdb);
+
if (from_tty && pattern && !any_matches)
gdb_printf ("No loaded shared libraries match the pattern `%s'.\n",
pattern);
diff --git a/gdb/solib.h b/gdb/solib.h
index 32101d27cc5acae22939eb6a9f72239aaf4812a4..2790603ea5bb3e0b0463c8c2254e69ea4e5c0a31 100644
--- a/gdb/solib.h
+++ b/gdb/solib.h
@@ -269,6 +269,10 @@ struct solib_ops
The TARGET string is used to identify the target. */
virtual gdb_bfd_ref_ptr bfd_open_from_target_memory
(CORE_ADDR addr, CORE_ADDR size, const char *target) const;
+
+ /* Acknowledge a library. This is called from add_solib after
+ loading symbols and placing breakpoints. */
+ virtual void ack_library (solib &so) const {}
};
/* A unique pointer to an solib_ops. */
diff --git a/gdb/target-delegates-gen.c b/gdb/target-delegates-gen.c
index 164ddbb9a2ea2da5fe4e02f78c3fa82d5e9eaf18..a311ff3daa8789524b18a52985c6e204f4c090a0 100644
--- a/gdb/target-delegates-gen.c
+++ b/gdb/target-delegates-gen.c
@@ -190,6 +190,8 @@ struct dummy_target : public target_ops
void call_history_from (ULONGEST arg0, int arg1, record_print_flags arg2) override;
void call_history_range (ULONGEST arg0, ULONGEST arg1, record_print_flags arg2) override;
bool augmented_libraries_svr4_read () override;
+ void ack_library (const char *arg0) override;
+ void ack_in_memory_library (CORE_ADDR arg0, CORE_ADDR arg1) override;
const struct frame_unwind *get_unwinder () override;
const struct frame_unwind *get_tailcall_unwinder () override;
void prepare_to_generate_core () override;
@@ -371,6 +373,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;
@@ -4202,6 +4206,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 f7c43f69a288728750b5dc85626742bca013e694..c141de22ef6b2138bf6aa357d1214e9c9ae43bc8 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -4237,6 +4237,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 365e894efe6918db9653218b178c9a36206829b1..70478834fe69f6720d91949cfe572e4fd900ee5f 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -1324,6 +1324,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. */
@@ -2660,4 +2675,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 /* GDB_TARGET_H */
diff --git a/gdbserver/dll.cc b/gdbserver/dll.cc
index c8e7c03012a6fd1650a7f7a4827f79b3735d624b..e6cb597ca90855e6932ed6c22c266ba1fefe3e31 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,8 +50,14 @@ 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);
/* We do not support duplicate in-memory libraries. */
@@ -61,7 +73,7 @@ loaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
error (_("Duplicate in-memory library; begin: %s, end: %s, base: %s."),
paddress (begin), paddress (end), paddress (base_addr));
- proc->all_dlls.emplace_back (begin, end, base_addr);
+ proc->all_dlls.emplace_back (begin, end, base_addr, need_ack);
proc->dlls_changed = true;
}
@@ -74,6 +86,74 @@ 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 == 0)
+ {
+ 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)
+ {
+ return ((dll.begin == begin) && (dll.end == end));
+ });
+
+ if (it != dlls.end ())
+ ack_dll (proc, *it);
+}
+
+void
+ack_dll (CORE_ADDR begin, CORE_ADDR end)
+{
+ ack_dll (current_process (), begin, end);
+}
+
static void
unload_dll_if (process_info *proc,
std::function<bool (const dll_info &)> pred)
@@ -95,6 +175,8 @@ unload_dll_if (process_info *proc,
{
/* 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;
}
diff --git a/gdbserver/dll.h b/gdbserver/dll.h
index a3af86dd6122ff8b8c47a335890f54a906f6d5be..8ddd323aa228fa02b1925e871313b07c3d1b3aa5 100644
--- a/gdbserver/dll.h
+++ b/gdbserver/dll.h
@@ -31,12 +31,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_)
{}
/* Where the library bits are stored. */
@@ -58,17 +61,28 @@ struct dll_info
/* The base address at which the library is loaded. */
CORE_ADDR base_addr;
+
+ /* Whether we need to acknowledge that we processed the library event,
+ e.g. that we inserted pending breakpoints. */
+ bool need_ack;
};
-extern void loaded_dll (const char *name, CORE_ADDR base_addr);
+/* Throws NOT_SUPPORTED_ERROR if library acknowledgement is requested
+ (NEED_ACK = TRUE) and not supported. */
+extern void loaded_dll (const char *name, CORE_ADDR base_addr,
+ bool need_ack = false);
extern void loaded_dll (process_info *proc, const char *name,
- CORE_ADDR base_addr);
+ CORE_ADDR base_addr, bool need_ack = false);
extern void loaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
- CORE_ADDR base_addr);
+ CORE_ADDR base_addr, bool need_ack = false);
extern void unloaded_dll (const char *name, CORE_ADDR base_addr);
extern void unloaded_dll (process_info *proc, const char *name,
CORE_ADDR base_addr);
extern void unloaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
CORE_ADDR base_addr);
+extern void ack_dll (const char *name);
+extern void ack_dll (process_info *proc, const char *name);
+extern void ack_dll (CORE_ADDR begin, CORE_ADDR end);
+extern void ack_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end);
#endif /* GDBSERVER_DLL_H */
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index aa695f2f2f80686ea6dc2ae3ae1bfd7c081067bf..1993a56fa78dc4b6dcd70beb0c1becddf34ff933 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -1944,18 +1944,22 @@ 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. */
try
{
dll_to_tmpfile (dll);
@@ -1968,9 +1972,13 @@ print_qxfer_libraries_entry (dll_info &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));
}
gdb_assert_not_reached ("unknown dll location: %x", dll.location);
@@ -2002,6 +2010,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);
@@ -2866,6 +2886,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. */
@@ -2994,6 +3018,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 ();
@@ -3381,6 +3408,100 @@ err:
return;
}
+/* Parse vAck packets. */
+
+static void
+handle_v_ack (char *own_buf)
+{
+ client_state &cs = get_client_state ();
+ const char *libstr = "library:";
+ const char *imlibstr = "in-memory-library:";
+
+ /* Move past vAck: to the first type string. */
+ char *p = &own_buf[5];
+ do
+ {
+ if (strncmp (p, libstr, strlen (libstr)) == 0)
+ {
+ if (!cs.vack_library_supported)
+ {
+ sprintf (own_buf,
+ "E.vAck:library packet not supported.");
+ return;
+ }
+
+ p += strlen (libstr);
+
+ /* We expect a single argument: the filename. */
+ const char *hex = p;
+
+ p = strchr (p, ';');
+ if (p == hex)
+ {
+ sprintf (own_buf, "E.missing argument in vAck:library.");
+ return;
+ }
+
+ if (p != nullptr)
+ *p++ = '\0';
+
+ std::string name = hex2str (hex);
+ if (strlen (hex) < (name.size () * 2))
+ {
+ sprintf (own_buf, "E.failed to convert library name '%s' in "
+ "vAck:library.", hex);
+ return;
+ }
+
+ ack_dll (name.c_str ());
+ }
+ else if (strncmp (p, imlibstr, strlen (imlibstr)) == 0)
+ {
+ if (!cs.vack_in_memory_library_supported)
+ {
+ sprintf (own_buf,
+ "E.vAck:in-memory-library packet not supported.");
+ return;
+ }
+
+ p += strlen (imlibstr);
+
+ /* We expect two arguments: begin and end address. */
+ CORE_ADDR begin, end;
+
+ begin = (CORE_ADDR) strtoull (p, &p, 16);
+ if ((*p == 0) || (*p == ';'))
+ {
+ sprintf (own_buf,
+ "E.missing argument in vAck:in-memory-library.");
+ return;
+ }
+
+ 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
@@ -3723,6 +3844,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 ebb5980f70dd778abfaade1086b4b89a529b4298..5552588c947a522b3aaa314015352d7e3d75c8b9 100644
--- a/gdbserver/server.h
+++ b/gdbserver/server.h
@@ -199,6 +199,10 @@ struct client_state
/* True if qXfer:libraries:read supports in-memory-library. */
bool in_memory_library_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 c400174c47cfd0e2ad218fbcd95abfd5b468be60..65308b78497cd235165b7e1368480bb4b0644f26 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 66ca72fec17ef16b5aa094c060b617813590b6fd..a98dd33676dc2156f94a08f93177ace3a0d5153d 100644
--- a/gdbserver/target.h
+++ b/gdbserver/target.h
@@ -530,6 +530,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;
@@ -727,6 +734,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] 92+ messages in thread* Re: [PATCH v3 10/44] gdb, gdbserver, rsp, ze: acknowledge libraries
2025-08-01 9:37 ` [PATCH v3 10/44] gdb, gdbserver, rsp, ze: acknowledge libraries Tankut Baris Aktemur
@ 2025-12-12 4:41 ` Simon Marchi
2025-12-12 14:28 ` Metzger, Markus T
0 siblings, 1 reply; 92+ messages in thread
From: Simon Marchi @ 2025-12-12 4:41 UTC (permalink / raw)
To: Tankut Baris Aktemur, gdb-patches, Markus Metzger
On 2025-08-01 05:37, Tankut Baris Aktemur wrote:
> 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.
I had trouble understanding the need for this until I remembered that
the remote inferior only has the GPU threads. So the thread loading the
library isn't even part of the inferior.
Otherwise, I think it would just naturally work:
- host thread hits magic breakpoint
- gdbserver fetches event, notices new library loaded, returns "library
loaded" event to gdb
- gdb loads the new solibs, then resumes the host thread
- gdbserver resumes the host thread, allowing the application to
use the newly loaded library
Simon
^ permalink raw reply [flat|nested] 92+ messages in thread
* RE: [PATCH v3 10/44] gdb, gdbserver, rsp, ze: acknowledge libraries
2025-12-12 4:41 ` Simon Marchi
@ 2025-12-12 14:28 ` Metzger, Markus T
0 siblings, 0 replies; 92+ messages in thread
From: Metzger, Markus T @ 2025-12-12 14:28 UTC (permalink / raw)
To: Simon Marchi; +Cc: Aktemur, Tankut Baris, gdb-patches
Hello Simon,
>> 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.
>
>I had trouble understanding the need for this until I remembered that
>the remote inferior only has the GPU threads. So the thread loading the
>library isn't even part of the inferior.
>
>Otherwise, I think it would just naturally work:
>
> - host thread hits magic breakpoint
> - gdbserver fetches event, notices new library loaded, returns "library
> loaded" event to gdb
> - gdb loads the new solibs, then resumes the host thread
> - gdbserver resumes the host thread, allowing the application to
> use the newly loaded library
Indeed, that's how it would work if we involved the host process.
In our case, however, the user-mode driver notifies the kernel-mode driver
about modules. The debugger will receive those as events.
To ensure that the debugger has had a chance to place breakpoints before
the host process submits those kernels, we require the debugger to acknowledge
such a module load event. The kernel mode driver will keep the host process
thread stopped in the module load ioctl() until the load has been acknowledged.
In your case, the breakpoint inside the host process loader does that.
Regards,
Markus.
Intel Deutschland GmbH
Registered Address: Dornacher Straße 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht München HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread
* [PATCH v3 11/44] gdb, solib, ze: update target_solib_ops::bfd_open_from_target_memory
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (9 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 10/44] gdb, gdbserver, rsp, ze: acknowledge libraries Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-12-12 4:43 ` Simon Marchi
2025-08-01 9:37 ` [PATCH v3 12/44] gdb, infrun, ze: allow saving process events Tankut Baris Aktemur
` (35 subsequent siblings)
46 siblings, 1 reply; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
From: Markus Metzger <markus.t.metzger@intel.com>
Update target_solib_ops::bfd_open_from_target_memory to mimic the
solib_bfd_open behavior on top of gdb_bfd_open for in-memory files.
Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
---
gdb/solib-target.c | 11 ++++++++++-
gdb/solib.c | 36 +++++++++++++++++++++++-------------
gdb/solib.h | 3 +++
3 files changed, 36 insertions(+), 14 deletions(-)
diff --git a/gdb/solib-target.c b/gdb/solib-target.c
index 304237e9d60c0661e65ea6ae86c165bd02d125e5..0b5ec206b0a19d146731a7c9eef48987b7103d1d 100644
--- a/gdb/solib-target.c
+++ b/gdb/solib-target.c
@@ -491,7 +491,16 @@ target_solib_ops::bfd_open_from_target_memory (CORE_ADDR addr,
CORE_ADDR size,
const char *target) const
{
- return gdb_bfd_open_from_target_memory (addr, size, target);
+ /* Open bfd for shared library. */
+ gdb_bfd_ref_ptr abfd
+ = gdb_bfd_open_from_target_memory (addr, size, target);
+ if (abfd == nullptr)
+ error (_("Could not open file from target '%s' "
+ "at address %s with size %s."), target,
+ core_addr_to_string_nz (addr), core_addr_to_string_nz (size));
+ solib_bfd_init (abfd.get ());
+
+ return abfd;
}
void
diff --git a/gdb/solib.c b/gdb/solib.c
index b7a552e80dc34ea5b69f55f4e25f5a390a57c2da..e554143dc18d380006a3917d13c1f31e423f09fc 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -427,13 +427,34 @@ solib_bfd_fopen (const char *pathname, int fd)
return abfd;
}
+/* See solib.h. */
+
+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
@@ -451,18 +472,7 @@ solib_bfd_open (const char *pathname)
/* Open bfd for shared library. */
gdb_bfd_ref_ptr abfd (solib_bfd_fopen (found_pathname.get (), found_file));
- /* Check bfd format. */
- if (!bfd_check_format (abfd.get (), bfd_object))
- error (_("`%s': not in executable format: %s"),
- bfd_get_filename (abfd.get ()), bfd_errmsg (bfd_get_error ()));
-
- /* Check bfd arch. */
- b = gdbarch_bfd_arch_info (current_inferior ()->arch ());
- if (!b->compatible (b, bfd_get_arch_info (abfd.get ())))
- error (_("`%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);
+ solib_bfd_init (abfd.get ());
return abfd;
}
diff --git a/gdb/solib.h b/gdb/solib.h
index 2790603ea5bb3e0b0463c8c2254e69ea4e5c0a31..ed8a1a89587087a99cefc9bd46793e2a13f8cf6f 100644
--- a/gdb/solib.h
+++ b/gdb/solib.h
@@ -292,6 +292,9 @@ 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);
+/* Initialize an opened BFD. */
+extern void solib_bfd_init (bfd *abfd);
+
/* Called when we free all symtabs of PSPACE, to free the shared library
information as well. */
--
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] 92+ messages in thread* Re: [PATCH v3 11/44] gdb, solib, ze: update target_solib_ops::bfd_open_from_target_memory
2025-08-01 9:37 ` [PATCH v3 11/44] gdb, solib, ze: update target_solib_ops::bfd_open_from_target_memory Tankut Baris Aktemur
@ 2025-12-12 4:43 ` Simon Marchi
2025-12-12 14:33 ` Metzger, Markus T
0 siblings, 1 reply; 92+ messages in thread
From: Simon Marchi @ 2025-12-12 4:43 UTC (permalink / raw)
To: Tankut Baris Aktemur, gdb-patches, Markus Metzger
On 2025-08-01 05:37, Tankut Baris Aktemur wrote:
> From: Markus Metzger <markus.t.metzger@intel.com>
>
> Update target_solib_ops::bfd_open_from_target_memory to mimic the
> solib_bfd_open behavior on top of gdb_bfd_open for in-memory files.
>
> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
It seems to me like this touches code added a few patches ago. Any
reason not to include that code right away?
Simon
^ permalink raw reply [flat|nested] 92+ messages in thread
* RE: [PATCH v3 11/44] gdb, solib, ze: update target_solib_ops::bfd_open_from_target_memory
2025-12-12 4:43 ` Simon Marchi
@ 2025-12-12 14:33 ` Metzger, Markus T
0 siblings, 0 replies; 92+ messages in thread
From: Metzger, Markus T @ 2025-12-12 14:33 UTC (permalink / raw)
To: Simon Marchi; +Cc: Aktemur, Tankut Baris, gdb-patches
Hello Simon,
>> From: Markus Metzger <markus.t.metzger@intel.com>
>>
>> Update target_solib_ops::bfd_open_from_target_memory to mimic the
>> solib_bfd_open behavior on top of gdb_bfd_open for in-memory files.
>>
>> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
>
>It seems to me like this touches code added a few patches ago. Any
>reason not to include that code right away?
No reason other than to keep patches small. I'll squash that one with 9.
Regards,
Markus.
Intel Deutschland GmbH
Registered Address: Dornacher Straße 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht München HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread
* [PATCH v3 12/44] gdb, infrun, ze: allow saving process events
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (10 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 11/44] gdb, solib, ze: update target_solib_ops::bfd_open_from_target_memory Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-12-12 4:57 ` Simon Marchi
2025-08-01 9:37 ` [PATCH v3 13/44] gdb, ze: add TARGET_WAITKIND_UNAVAILABLE Tankut Baris Aktemur
` (34 subsequent siblings)
46 siblings, 1 reply; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
From: Markus Metzger <markus.t.metzger@intel.com>
When a process stop reply is encountered during stop_all_threads (), a new
thread with the process' ptid is created and the waitstatus is saved in
that thread. The effect is that we have a thread with a process ptid and
we dropped the event.
Save the waitstatus in the inferior, prevent that inferior from resuming,
and return it on the next wait.
---
gdb/inferior.h | 4 ++++
gdb/infrun.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 55 insertions(+)
diff --git a/gdb/inferior.h b/gdb/inferior.h
index fe94e289bdc741e4763461e2df44e4153b6f5e12..ef6d236b0f72638879b33f1894fbf3d1d7050816 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -64,6 +64,7 @@ struct thread_info;
#include "displaced-stepping.h"
#include "gdbsupport/unordered_map.h"
+#include <optional>
struct infcall_suspend_state;
struct infcall_control_state;
@@ -326,6 +327,9 @@ struct inferior_control_state
/* See the definition of stop_kind above. */
enum stop_kind stop_soon;
+
+ /* The waitstatus for this inferior's last event. */
+ std::optional<target_waitstatus> waitstatus;
};
/* Initialize the inferior-related global state. */
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 9d3e1b7782e4ddf84089156e910f77c6396586a4..4483082c899c5648f8693636ebe2af0d47da238e 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -2654,6 +2654,27 @@ resume_1 (enum gdb_signal sig)
gdb_assert (!tp->stop_requested);
gdb_assert (!thread_is_in_step_over_chain (tp));
+ inferior *inf = tp->inf;
+ process_stratum_target *target = inf->process_target ();
+ if (inf->control.waitstatus.has_value ())
+ {
+ infrun_debug_printf
+ ("inferior %s has pending wait status %s.",
+ target_pid_to_str (ptid_t (inf->pid)).c_str (),
+ inf->control.waitstatus->to_string ().c_str ());
+
+ target->threads_executing = true;
+
+ if (target_can_async_p ())
+ {
+ target_async (1);
+ /* Tell the event loop we have an event to process. */
+ mark_async_event_handler (infrun_async_inferior_event_token);
+ }
+
+ return;
+ }
+
if (tp->has_pending_waitstatus ())
{
infrun_debug_printf
@@ -4043,6 +4064,20 @@ do_target_wait_1 (inferior *inf, ptid_t ptid,
the wait code relies on it - doing so is always a mistake. */
switch_to_inferior_no_thread (inf);
+ /* Check if we have any saved waitstatus for the inferior itself. */
+ if (inf->control.waitstatus.has_value ())
+ {
+ /* Wake up the event loop again, until all pending events are
+ processed. */
+ if (target_is_async_p ())
+ mark_async_event_handler (infrun_async_inferior_event_token);
+
+ *status = *inf->control.waitstatus;
+ inf->control.waitstatus.reset ();
+
+ return ptid_t (inf->pid);
+ }
+
/* First check if there is a resumed thread with a wait status
pending. */
if (ptid == minus_one_ptid || ptid.is_pid ())
@@ -5501,6 +5536,22 @@ handle_one (const wait_one_event &event)
}
}
}
+ else if (event.ptid.is_pid ())
+ {
+ /* This may be the first time we see the inferior report
+ a stop. */
+ inferior *inf = find_inferior_ptid (event.target, event.ptid);
+ if (inf->needs_setup)
+ setup_inferior (0);
+
+ /* This is needed to mark all the relevant threads in
+ case the event is received from an all-stop
+ target. */
+ mark_non_executing_threads (event.target, event.ptid, event.ws);
+
+ /* Save the waitstatus for later. */
+ *inf->control.waitstatus = event.ws;
+ }
else
{
thread_info *t = event.target->find_thread (event.ptid);
--
2.34.1
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread* Re: [PATCH v3 12/44] gdb, infrun, ze: allow saving process events
2025-08-01 9:37 ` [PATCH v3 12/44] gdb, infrun, ze: allow saving process events Tankut Baris Aktemur
@ 2025-12-12 4:57 ` Simon Marchi
2025-12-15 13:13 ` Metzger, Markus T
0 siblings, 1 reply; 92+ messages in thread
From: Simon Marchi @ 2025-12-12 4:57 UTC (permalink / raw)
To: Tankut Baris Aktemur, gdb-patches, Markus Metzger
On 2025-08-01 05:37, 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.
Can you clarify what a process-wide event is? Are there instances where
a target returns that today, or is something new with intelgt?
Simon
^ permalink raw reply [flat|nested] 92+ messages in thread
* RE: [PATCH v3 12/44] gdb, infrun, ze: allow saving process events
2025-12-12 4:57 ` Simon Marchi
@ 2025-12-15 13:13 ` Metzger, Markus T
2025-12-16 21:10 ` Simon Marchi
0 siblings, 1 reply; 92+ messages in thread
From: Metzger, Markus T @ 2025-12-15 13:13 UTC (permalink / raw)
To: Simon Marchi; +Cc: Aktemur, Tankut Baris, gdb-patches
Hello Simon,
>> 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.
>
>Can you clarify what a process-wide event is? Are there instances where
>a target returns that today, or is something new with intelgt?
This is something new with IntelGT. An example would be a module load event.
Regards,
Markus.
Intel Deutschland GmbH
Registered Address: Dornacher Straße 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht München HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread
* Re: [PATCH v3 12/44] gdb, infrun, ze: allow saving process events
2025-12-15 13:13 ` Metzger, Markus T
@ 2025-12-16 21:10 ` Simon Marchi
2025-12-17 9:30 ` Metzger, Markus T
0 siblings, 1 reply; 92+ messages in thread
From: Simon Marchi @ 2025-12-16 21:10 UTC (permalink / raw)
To: Metzger, Markus T; +Cc: Aktemur, Tankut Baris, gdb-patches
On 2025-12-15 08:13, Metzger, Markus T wrote:
> Hello Simon,
>
>>> 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.
>>
>> Can you clarify what a process-wide event is? Are there instances where
>> a target returns that today, or is something new with intelgt?
>
> This is something new with IntelGT. An example would be a module load event.
Can you point me where this process-wide module load event is added in
the series? All I can find is the `U` stop reply, which can have
`;library` appended, but I presume that `U` is a thread-specific event.
Simon
^ permalink raw reply [flat|nested] 92+ messages in thread
* RE: [PATCH v3 12/44] gdb, infrun, ze: allow saving process events
2025-12-16 21:10 ` Simon Marchi
@ 2025-12-17 9:30 ` Metzger, Markus T
2025-12-17 20:44 ` Simon Marchi
0 siblings, 1 reply; 92+ messages in thread
From: Metzger, Markus T @ 2025-12-17 9:30 UTC (permalink / raw)
To: Simon Marchi; +Cc: Aktemur, Tankut Baris, 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.
>>>
>>> Can you clarify what a process-wide event is? Are there instances where
>>> a target returns that today, or is something new with intelgt?
>>
>> This is something new with IntelGT. An example would be a module load
>event.
>
>Can you point me where this process-wide module load event is added in
>the series? All I can find is the `U` stop reply, which can have
>`;library` appended, but I presume that `U` is a thread-specific event.
This is added in
[PATCH v3 35/44] gdbserver, ze, intelgt: introduce ze-low and intel-ze-low targets
The intelgt remote target may also send
[remote] Notification received: Stop:Up1.0;library
Regards,
Markus.
Intel Deutschland GmbH
Registered Address: Dornacher Straße 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht München HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread
* Re: [PATCH v3 12/44] gdb, infrun, ze: allow saving process events
2025-12-17 9:30 ` Metzger, Markus T
@ 2025-12-17 20:44 ` Simon Marchi
2025-12-18 7:20 ` Metzger, Markus T
0 siblings, 1 reply; 92+ messages in thread
From: Simon Marchi @ 2025-12-17 20:44 UTC (permalink / raw)
To: Metzger, Markus T; +Cc: Aktemur, Tankut Baris, gdb-patches
On 12/17/25 4:30 AM, Metzger, Markus T 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.
>>>>
>>>> Can you clarify what a process-wide event is? Are there instances where
>>>> a target returns that today, or is something new with intelgt?
>>>
>>> This is something new with IntelGT. An example would be a module load
>> event.
>>
>> Can you point me where this process-wide module load event is added in
>> the series? All I can find is the `U` stop reply, which can have
>> `;library` appended, but I presume that `U` is a thread-specific event.
>
> This is added in
>
> [PATCH v3 35/44] gdbserver, ze, intelgt: introduce ze-low and intel-ze-low targets
>
> The intelgt remote target may also send
>
> [remote] Notification received: Stop:Up1.0;library
Ok, so IIUC, it is an abuse (or re-purpose) of the U stop reply to be
able to tack the "library" part? My understanding is that the original
intent of the U stop reply is to say that we tried to stop a specific
thread but that it isn't stoppable. On the other hand, returning a
process-wide U does not mean "the whole process is unavailable", or does
it? I assume it does not change the state of all threads in the process
to "unavailable".
Simon
^ permalink raw reply [flat|nested] 92+ messages in thread
* RE: [PATCH v3 12/44] gdb, infrun, ze: allow saving process events
2025-12-17 20:44 ` Simon Marchi
@ 2025-12-18 7:20 ` Metzger, Markus T
0 siblings, 0 replies; 92+ messages in thread
From: Metzger, Markus T @ 2025-12-18 7:20 UTC (permalink / raw)
To: Simon Marchi; +Cc: Aktemur, Tankut Baris, 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.
>>>>>
>>>>> Can you clarify what a process-wide event is? Are there instances where
>>>>> a target returns that today, or is something new with intelgt?
>>>>
>>>> This is something new with IntelGT. An example would be a module load
>>> event.
>>>
>>> Can you point me where this process-wide module load event is added in
>>> the series? All I can find is the `U` stop reply, which can have
>>> `;library` appended, but I presume that `U` is a thread-specific event.
>>
>> This is added in
>>
>> [PATCH v3 35/44] gdbserver, ze, intelgt: introduce ze-low and intel-ze-low
>targets
>>
>> The intelgt remote target may also send
>>
>> [remote] Notification received: Stop:Up1.0;library
>
>Ok, so IIUC, it is an abuse (or re-purpose) of the U stop reply to be
>able to tack the "library" part? My understanding is that the original
>intent of the U stop reply is to say that we tried to stop a specific
>thread but that it isn't stoppable. On the other hand, returning a
>process-wide U does not mean "the whole process is unavailable", or does
>it? I assume it does not change the state of all threads in the process
>to "unavailable".
If there are thread events, we attach the ;library to one of those.
If all threads are either running or unavailable, we still need to tell GDB
about the new library and we chose the U stop reply we introduced
before. This seemed the easiest solution.
A typical scenario is when the host process starts up, before any work
can be submitted to the device, we need to load device libraries. At that
time, all device threads are unavailable.
Regards,
Markus.
Intel Deutschland GmbH
Registered Address: Dornacher Straße 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht München HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread
* [PATCH v3 13/44] gdb, ze: add TARGET_WAITKIND_UNAVAILABLE
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (11 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 12/44] gdb, infrun, ze: allow saving process events Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 14/44] gdb, infrun, ze: handle stopping unavailable threads Tankut Baris Aktemur
` (33 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
From: Markus Metzger <markus.t.metzger@intel.com>
This new WAITKIND means that we cannot interact with the thread at the
moment. The thread may become available again at a later time.
This will be used to model idle threads on Intel GT devices.
---
gdb/fork-child.c | 10 +++++++---
gdb/gdbthread.h | 12 +++++++++---
gdb/infrun.c | 22 +++++++++++++++++++++-
gdb/nat/fork-inferior.c | 10 ++++++++++
gdb/remote.c | 6 +++++-
gdb/target/waitstatus.c | 1 +
gdb/target/waitstatus.h | 22 ++++++++++++++++++++++
gdb/thread.c | 2 +-
8 files changed, 76 insertions(+), 9 deletions(-)
diff --git a/gdb/fork-child.c b/gdb/fork-child.c
index fd7f1e0d16bb1b6e1e889b16d42bde84d150d86b..f5ac1c13ef61968bd182fbe6df650a6ff2c8d4a2 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 c561e9a7b64d9246a08ea00b209dfc936c517b59..52c5c3ac43d6ffcb126d80a2cf7c9bcf3af2abf9 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 4483082c899c5648f8693636ebe2af0d47da238e..65392bc3b6f063fe65320d55b990b64a68a638a4 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -5448,7 +5448,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);
@@ -6638,6 +6650,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 b6cf410ac9c413008fce9bdb6f921a52a4428bf6..1e42c42cf9fe54ed2d77d2ce253712146804d17e 100644
--- a/gdb/nat/fork-inferior.c
+++ b/gdb/nat/fork-inferior.c
@@ -480,6 +480,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 a4daca885f77803374384adbe13524af3e62b12b..b0e70e1517d6022ca7aba3b63ec34ddf90c85b7b 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -4992,7 +4992,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 94d129a786f81edb27c36e85aff1fcfa32e7af23..4576e5a4553125fa7c13422f9ac17ce23cf416d6 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 d87988f9b77d6c8353db275ace8c18149a792c85..5b897256c1758cb4c6f32ce0ec1240e5876d09c4 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 920d8dc07a8551a299b475526fa0ac60588b0c32..2bff29ed6646e4a2f08fc641954f6ef6c2184701 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] 92+ messages in thread* [PATCH v3 14/44] gdb, infrun, ze: handle stopping unavailable threads
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (12 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 13/44] gdb, ze: add TARGET_WAITKIND_UNAVAILABLE Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 15/44] gdb, infrun, ze: allow resuming " Tankut Baris Aktemur
` (32 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
From: Markus Metzger <markus.t.metzger@intel.com>
On GPUs, threads may not always respond to interrupt requests. In order
to still be able to support all-stop model on those targets, we allow this
case and model such threads as unavailable.
They do exist but we cannot interact with them. In particular, they do
not have registers, including PC.
Use regcache_read_pc_protected () instead of regcache_read_pc () to
suppress an error when trying to access an unavailable thread's registers.
The thread's waitstatus will be TARGET_WAITKIND_UNAVAILABLE so there is
nothing extra to do here.
---
gdb/infrun.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 65392bc3b6f063fe65320d55b990b64a68a638a4..dc55e17b98a13dac14171a795574344ec2a3c545 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -5624,7 +5624,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)",
@@ -6251,7 +6251,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] 92+ messages in thread* [PATCH v3 15/44] gdb, infrun, ze: allow resuming unavailable threads
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (13 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 14/44] gdb, infrun, ze: handle stopping unavailable threads Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 16/44] gdb, gdbserver, ze: add U stop reply Tankut Baris Aktemur
` (31 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
From: Markus Metzger <markus.t.metzger@intel.com>
On GPUs, threads may not always respond to interrupt requests when we try
to stabilize threads on an all-stop target. In order to still be able to
support all-stop model on those targets, we allow this case and model such
threads as unavailable.
They do exist but we cannot interact with them. In particular, they do
not have registers, including PC.
In some cases, all threads on a target may be unavailable. This can
happen when we currently do not have any work scheduled on that device.
One particular instance of this is during startup when we newly attach to
the device.
We still need to allow 'continuing' those threads to make GDB listen to
events from that target.
Update gdb.threads/killed-outside.exp by adding another case to the
expected messages when killing the inferior. It already contained cases
with and without thread exited messages. Add another one for an exited
thread but no pc-not-available message.
---
gdb/infrun.c | 11 ++++++++++-
gdb/testsuite/gdb.threads/killed-outside.exp | 4 ++++
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/gdb/infrun.c b/gdb/infrun.c
index dc55e17b98a13dac14171a795574344ec2a3c545..0ab642abfe246d87179c296401b5cf5a2c3003dd 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -2730,7 +2730,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 9edb8d251b5737dbc5dfd55ce0245beb7be3a458..b0ca478bc3d42540057322bc30c22cdc07792665 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] 92+ messages in thread* [PATCH v3 16/44] gdb, gdbserver, ze: add U stop reply
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (14 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 15/44] gdb, infrun, ze: allow resuming " Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 17/44] gdb, gdbserver, ze: add library notification to " Tankut Baris Aktemur
` (30 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
From: Markus Metzger <markus.t.metzger@intel.com>
Add a new stop reply U for unavailable saying that we tried to stop a
process or thread but it would not respond and we cannot afford waiting.
This may occur when modeling threads as hardware threads on GPUs, where
threads that are currently idle cannot be interacted with. We cannot
afford waiting for threads to become available again as that may require
submitting new work from the host process. Or it may never happen for
some devices that simply are not used (anymore).
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
gdb/NEWS | 6 ++++++
gdb/doc/gdb.texinfo | 24 ++++++++++++++++++++++++
gdb/remote.c | 18 +++++++++++++++++-
gdbserver/remote-utils.cc | 5 +++++
gdbserver/server.cc | 28 ++++++++++++++++++++++++++++
5 files changed, 80 insertions(+), 1 deletion(-)
diff --git a/gdb/NEWS b/gdb/NEWS
index e95550496c4190f7032e917329ce497844a79e67..e281347eac138fd8f7c559a4cfed389b5b61e92b 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -209,6 +209,12 @@ vAck:in-memory-library
after the initial processing by GDB such as loading symbols and placing
breakpoints.
+U stop reply
+
+ Indicates that threads are currently unavailable. We tried stopping them but
+ they did not respond. The remote stub reports support for this stop reply to
+ GDB's qSupported query.
+
* Changed remote packets
qXfer:threads:read
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index faa93177e0e9f0a6b83ec9dda1031276ae1c114d..634675242bd4ff72b6742597133e7ad28d6768da 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -24882,6 +24882,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.
+
@item @code{qXfer:libraries:read:in-memory-library}
@tab @code{in-memory-library library elements}
@tab Support for in-memory libraries.
@@ -44149,6 +44153,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
@@ -45378,6 +45393,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:
@@ -45627,6 +45647,10 @@ if it sent the @samp{error-message} feature.
@item binary-upload
The remote stub supports the @samp{x} packet (@pxref{x packet}).
+
+@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 b0e70e1517d6022ca7aba3b63ec34ddf90c85b7b..461bbfb783019e433a3a0378b3162444edc30dce 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -407,6 +407,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
};
@@ -5881,6 +5884,8 @@ static const struct protocol_feature remote_protocol_features[] = {
PACKET_vAck_library },
{ "vAck:in-memory-library", PACKET_DISABLE, remote_supported_packet,
PACKET_vAck_in_memory_library },
+ { "unavailable", PACKET_DISABLE, remote_supported_packet,
+ PACKET_unavailable },
};
static char *remote_support_xml;
@@ -5992,6 +5997,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+");
@@ -8377,6 +8386,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;
}
}
@@ -8790,7 +8803,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;
@@ -16661,6 +16674,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 15f073dd6bece9225e03c4a0ab47b17f64ca3b72..6b4d15763afc8c31ecf05b902319b0a2c1427aa3 100644
--- a/gdbserver/remote-utils.cc
+++ b/gdbserver/remote-utils.cc
@@ -1287,6 +1287,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 1993a56fa78dc4b6dcd70beb0c1becddf34ff933..5fd804750bc878e36b4b825edad244c88d1b0253 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -88,6 +88,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;
@@ -2884,6 +2887,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+")
@@ -3545,6 +3554,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
@@ -5283,6 +5302,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] 92+ messages in thread* [PATCH v3 17/44] gdb, gdbserver, ze: add library notification to U stop reply
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (15 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 16/44] gdb, gdbserver, ze: add U stop reply Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 18/44] gdbserver, ze: report TARGET_WAITKIND_UNAVAILABLE events Tankut Baris Aktemur
` (29 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
From: Markus Metzger <markus.t.metzger@intel.com>
If libraries have changed, add a ";library" notification to a 'U' stop
reply packet and handle it at the GDB side.
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
gdb/doc/gdb.texinfo | 5 +++++
gdb/remote.c | 11 +++++++++--
gdbserver/remote-utils.cc | 22 +++++++++++++++++++---
3 files changed, 33 insertions(+), 5 deletions(-)
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 634675242bd4ff72b6742597133e7ad28d6768da..de9c49d3f9193f768e899ea22eb4b10cf78ea337 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -44154,10 +44154,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 461bbfb783019e433a3a0378b3162444edc30dce..385618fcbd91dcfab2fd55b534eec303c689cfed 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -8387,8 +8387,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 6b4d15763afc8c31ecf05b902319b0a2c1427aa3..56fb9c86742c160318502eab4d43ca829d5c593a 100644
--- a/gdbserver/remote-utils.cc
+++ b/gdbserver/remote-utils.cc
@@ -1288,9 +1288,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] 92+ messages in thread* [PATCH v3 18/44] gdbserver, ze: report TARGET_WAITKIND_UNAVAILABLE events
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (16 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 17/44] gdb, gdbserver, ze: add library notification to " Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 19/44] gdb, ze: handle TARGET_WAITKIND_UNAVAILABLE in stop_all_threads Tankut Baris Aktemur
` (28 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
Do not ignore a TARGET_WAITKIND_UNAVAILABLE event. This could be the result of
1. GDB sending a stop request, but the thread being unavailable.
2. target generating a library load event for which there is no
actual stop event.
In case 1, if we do not inform GDB, it would wait indefinitely for the
thread it attempted to stop.
In case 2, if we do not inform GDB, the target may wait for an ack
which will never come from GDB.
---
gdbserver/server.cc | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 5fd804750bc878e36b4b825edad244c88d1b0253..4c12c487bcdb57baf8ea729d6e4f73e9f335e3a9 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -5304,12 +5304,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] 92+ messages in thread* [PATCH v3 19/44] gdb, ze: handle TARGET_WAITKIND_UNAVAILABLE in stop_all_threads
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (17 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 18/44] gdbserver, ze: report TARGET_WAITKIND_UNAVAILABLE events Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 20/44] gdb, remote: handle thread unavailability in print_one_stopped_thread Tankut Baris Aktemur
` (27 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
If a TARGET_WAITKIND_UNAVAILABLE is received as the result of a stop
request, consider it the same as a stopped event with the 0 signal, and
do not save it as a pending event. We would expect the target to send us
a TARGET_WAITKIND_STOPPED status if there were a stopped event. We must
have received TARGET_WAITKIND_UNAVAILABLE because all the threads were
unavailable for our interrupt. Hence, it must be OK to treat the
TARGET_WAITKIND_UNAVAILABLE status as an internal stop and not report
to the user.
---
gdb/infrun.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 0ab642abfe246d87179c296401b5cf5a2c3003dd..ae0575c60cd087a222e30834b4aa8c317a271afe 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -5592,8 +5592,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] 92+ messages in thread* [PATCH v3 20/44] gdb, remote: handle thread unavailability in print_one_stopped_thread
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (18 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 19/44] gdb, ze: handle TARGET_WAITKIND_UNAVAILABLE in stop_all_threads Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 21/44] gdb, remote: do 'remote_add_inferior' in 'remote_notice_new_inferior' earlier Tankut Baris Aktemur
` (26 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
Take it into account in remote_target::print_one_stopped_thread that
the thread may be unavailable.
---
gdb/remote.c | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/gdb/remote.c b/gdb/remote.c
index 385618fcbd91dcfab2fd55b534eec303c689cfed..5a1090d2af191ddb061fa90b55a01d7548e37c07 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -4899,23 +4899,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] 92+ messages in thread* [PATCH v3 21/44] gdb, remote: do 'remote_add_inferior' in 'remote_notice_new_inferior' earlier
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (19 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 20/44] gdb, remote: handle thread unavailability in print_one_stopped_thread Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 22/44] gdb, remote: handle a generic process PID in remote_notice_new_inferior Tankut Baris Aktemur
` (25 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
In remote_target::remote_notice_new_inferior, we check if the inferior
for the reported ptid exists, and if not, we add that inferior via
'remote_add_inferior'. However, before adding the new inferior, we
may have already added a new thread, because the thread is not found
via 'in_thread_list'. This may bring the odd situation that a new
thread is being added before its inferior exists. Fix this problem by
moving the check and creation of a new inferior to an earlier spot in
the execution flow.
Note that the code has a check for inferior_ptid.is_pid, but this is
not useful to avoid the problem described above because the core
infrun clears inferior_ptid (i.e. sets to null_ptid) before waiting on
targets for stop events.
---
gdb/remote.c | 23 +++++++++++------------
1 file changed, 11 insertions(+), 12 deletions(-)
diff --git a/gdb/remote.c b/gdb/remote.c
index 5a1090d2af191ddb061fa90b55a01d7548e37c07..0b36795436a80d861c3eea389e71bf48da97228d 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -3000,6 +3000,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 ())
{
@@ -3030,18 +3041,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] 92+ messages in thread* [PATCH v3 22/44] gdb, remote: handle a generic process PID in remote_notice_new_inferior
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (20 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 21/44] gdb, remote: do 'remote_add_inferior' in 'remote_notice_new_inferior' earlier Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 23/44] gdb, remote: handle a generic process PID in process_stop_reply Tankut Baris Aktemur
` (24 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
If the ptid received by 'remote_notice_new_inferior' is a generic pid,
return after checking for a new inferior. Do not attempt checking for
and adding new threads.
---
gdb/remote.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/gdb/remote.c b/gdb/remote.c
index 0b36795436a80d861c3eea389e71bf48da97228d..95bd7dd944c268b658489e40da27ce2d473f43ff 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -3011,6 +3011,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] 92+ messages in thread* [PATCH v3 23/44] gdb, remote: handle a generic process PID in process_stop_reply
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (21 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 22/44] gdb, remote: handle a generic process PID in remote_notice_new_inferior Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 24/44] gdb: use the pid from inferior in setup_inferior Tankut Baris Aktemur
` (23 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
When processing a new stop reply, the received PID could be a generic
process id (i.e.: is_pid() == true) instead of a specific thread id.
Handle this case.
---
gdb/remote.c | 25 +++++++++++++++----------
1 file changed, 15 insertions(+), 10 deletions(-)
diff --git a/gdb/remote.c b/gdb/remote.c
index 95bd7dd944c268b658489e40da27ce2d473f43ff..e8d3b0caff42bd8add486e3546b3275bc578efb4 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -8654,18 +8654,23 @@ remote_target::process_stop_reply (stop_reply_up stop_reply,
}
}
- remote_thread_info *remote_thr = get_remote_thread_info (this, ptid);
- remote_thr->core = stop_reply->core;
- remote_thr->stop_reason = stop_reply->stop_reason;
- remote_thr->watch_data_address = stop_reply->watch_data_address;
-
- if (target_is_non_stop_p ())
+ if (!ptid.is_pid ())
{
- /* If the target works in non-stop mode, a stop-reply indicates that
- only this thread stopped. */
- remote_thr->set_not_resumed ();
+ remote_thread_info *remote_thr
+ = get_remote_thread_info (this, ptid);
+ remote_thr->core = stop_reply->core;
+ remote_thr->stop_reason = stop_reply->stop_reason;
+ remote_thr->watch_data_address = stop_reply->watch_data_address;
+
+ if (target_is_non_stop_p ())
+ {
+ /* If the target works in non-stop mode, a stop-reply
+ indicates that only this thread stopped. */
+ remote_thr->set_not_resumed ();
+ }
}
- else
+
+ if (!target_is_non_stop_p ())
{
/* If the target works in all-stop mode, a stop-reply indicates that
all the target's threads stopped. */
--
2.34.1
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread* [PATCH v3 24/44] gdb: use the pid from inferior in setup_inferior
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (22 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 23/44] gdb, remote: handle a generic process PID in process_stop_reply Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-12-12 19:51 ` Simon Marchi
2025-08-01 9:37 ` [PATCH v3 25/44] gdb: revise the pid_to_exec_file target op Tankut Baris Aktemur
` (22 subsequent siblings)
46 siblings, 1 reply; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
This is a step to reduce the dependency on the global inferior_ptid
variable.
---
gdb/infcmd.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 8978c8a63038d1db39168cc47c49e738863e7f36..1d5bed1bb17015271430255d0d6e288d0edfc791 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -2497,7 +2497,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 ();
@@ -2505,7 +2505,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, true);
}
--
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] 92+ messages in thread* Re: [PATCH v3 24/44] gdb: use the pid from inferior in setup_inferior
2025-08-01 9:37 ` [PATCH v3 24/44] gdb: use the pid from inferior in setup_inferior Tankut Baris Aktemur
@ 2025-12-12 19:51 ` Simon Marchi
2025-12-13 12:40 ` Aktemur, Tankut Baris
0 siblings, 1 reply; 92+ messages in thread
From: Simon Marchi @ 2025-12-12 19:51 UTC (permalink / raw)
To: Tankut Baris Aktemur, gdb-patches, Markus Metzger
On 8/1/25 5:37 AM, Tankut Baris Aktemur wrote:
> This is a step to reduce the dependency on the global inferior_ptid
> variable.
I think that this patch can be pushed on its own.
Approved-By: Simon Marchi <simon.marchi@efficios.com>
Simon
^ permalink raw reply [flat|nested] 92+ messages in thread
* RE: [PATCH v3 24/44] gdb: use the pid from inferior in setup_inferior
2025-12-12 19:51 ` Simon Marchi
@ 2025-12-13 12:40 ` Aktemur, Tankut Baris
0 siblings, 0 replies; 92+ messages in thread
From: Aktemur, Tankut Baris @ 2025-12-13 12:40 UTC (permalink / raw)
To: Simon Marchi, gdb-patches, Metzger, Markus T
On Friday, December 12, 2025 8:51 PM, Simon Marchi wrote:
> On 8/1/25 5:37 AM, Tankut Baris Aktemur wrote:
> > This is a step to reduce the dependency on the global inferior_ptid
> > variable.
>
> I think that this patch can be pushed on its own.
>
> Approved-By: Simon Marchi <simon.marchi@efficios.com>
>
> Simon
Thank you. I pushed this.
-Baris
Intel Deutschland GmbH
Registered Address: Dornacher Straße 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht München HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread
* [PATCH v3 25/44] gdb: revise the pid_to_exec_file target op
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (23 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 24/44] gdb: use the pid from inferior in setup_inferior Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 26/44] gdb: load solibs if the target does not have the notion of an exec file Tankut Baris Aktemur
` (21 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
Some targets, such as Intel GT, do not have the notion of an exec
file. These targets may leave the pid_to_exec_file target op
unimplemented, but from GDB's point of view, this means the exec file
could not be determined, and GDB issues a warning.
Extend the meaning of the pid_to_exec_file target op, so that if the
exec file path is empty, it means that the target does not have exec
files at all. This way, GDB can omit checking the exec file while
also not issuing the warning.
---
gdb/exec.c | 6 ++++++
gdb/target.h | 3 +++
2 files changed, 9 insertions(+)
diff --git a/gdb/exec.c b/gdb/exec.c
index c2a1f8a7adaa06768017fd8736d40ec671d467bb..4b2abd499d8e8bbe335000cfe49855a4284981fc 100644
--- a/gdb/exec.c
+++ b/gdb/exec.c
@@ -336,6 +336,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);
if (exec_file_host == nullptr)
diff --git a/gdb/target.h b/gdb/target.h
index 70478834fe69f6720d91949cfe572e4fd900ee5f..eb5d8e41805c0cc915aafb47f32217d36da7bcc2 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -2027,6 +2027,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] 92+ messages in thread* [PATCH v3 26/44] gdb: load solibs if the target does not have the notion of an exec file
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (24 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 25/44] gdb: revise the pid_to_exec_file target op Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-12-12 20:30 ` Simon Marchi
2025-08-01 9:37 ` [PATCH v3 27/44] gdbserver: import AC_LIB_HAVE_LINKFLAGS macro into the autoconf script Tankut Baris Aktemur
` (20 subsequent siblings)
46 siblings, 1 reply; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
In the 'post_create_inferior' function, the code block that calls the
solib ops' post-create-inferior hook function is guarded by a
null-check on exec_bfd. This prevents loading solibs of an inferior
if it does not have an exec_bfd. However, such a scenario could be
possible with relatively newer targets, such as GPUs, where an
executable does not exist but the GPU kernels are treated as
libraries.
Add a check for this case.
---
gdb/infcmd.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 1d5bed1bb17015271430255d0d6e288d0edfc791..bab6fe0db1329c119d94e13e12030662f4b15f1b 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -262,7 +262,15 @@ post_create_inferior (int from_tty, bool set_pspace_solib_ops)
current_program_space->set_solib_ops
(gdbarch_make_solib_ops (current_inferior ()->arch ()));
- if (current_program_space->exec_bfd ())
+ /* Some targets (e.g. GPUs) may still have solibs although they do
+ not have the notion of an exec file. */
+ const char *exec_file
+ = target_pid_to_exec_file (current_inferior ()->pid);
+ bool target_may_have_solibs
+ = (exec_file != nullptr && *exec_file == '\0');
+
+ if (current_program_space->exec_bfd () != nullptr
+ || target_may_have_solibs)
{
const unsigned solib_add_generation
= current_program_space->solib_add_generation;
--
2.34.1
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread* Re: [PATCH v3 26/44] gdb: load solibs if the target does not have the notion of an exec file
2025-08-01 9:37 ` [PATCH v3 26/44] gdb: load solibs if the target does not have the notion of an exec file Tankut Baris Aktemur
@ 2025-12-12 20:30 ` Simon Marchi
2026-01-09 19:10 ` Aktemur, Tankut Baris
0 siblings, 1 reply; 92+ messages in thread
From: Simon Marchi @ 2025-12-12 20:30 UTC (permalink / raw)
To: Tankut Baris Aktemur, gdb-patches, Markus Metzger
On 8/1/25 5:37 AM, Tankut Baris Aktemur wrote:
> In the 'post_create_inferior' function, the code block that calls the
> solib ops' post-create-inferior hook function is guarded by a
> null-check on exec_bfd. This prevents loading solibs of an inferior
> if it does not have an exec_bfd. However, such a scenario could be
> possible with relatively newer targets, such as GPUs, where an
> executable does not exist but the GPU kernels are treated as
> libraries.
>
> Add a check for this case.
Could you check this patch (and series while at it)? It was written for
a different reason, but I think it will address you issue. It gets rid
of that "if" entirely.
https://inbox.sourceware.org/gdb-patches/20250930185430.1614365-4-sdarche@efficios.com/
Simon
^ permalink raw reply [flat|nested] 92+ messages in thread
* RE: [PATCH v3 26/44] gdb: load solibs if the target does not have the notion of an exec file
2025-12-12 20:30 ` Simon Marchi
@ 2026-01-09 19:10 ` Aktemur, Tankut Baris
0 siblings, 0 replies; 92+ messages in thread
From: Aktemur, Tankut Baris @ 2026-01-09 19:10 UTC (permalink / raw)
To: Simon Marchi, gdb-patches, Metzger, Markus T
On Friday, December 12, 2025 9:31 PM, Simon Marchi wrote:
> On 8/1/25 5:37 AM, Tankut Baris Aktemur wrote:
> > In the 'post_create_inferior' function, the code block that calls the
> > solib ops' post-create-inferior hook function is guarded by a
> > null-check on exec_bfd. This prevents loading solibs of an inferior
> > if it does not have an exec_bfd. However, such a scenario could be
> > possible with relatively newer targets, such as GPUs, where an
> > executable does not exist but the GPU kernels are treated as
> > libraries.
> >
> > Add a check for this case.
>
> Could you check this patch (and series while at it)? It was written for
> a different reason, but I think it will address you issue. It gets rid
> of that "if" entirely.
>
> https://inbox.sourceware.org/gdb-patches/20250930185430.1614365-4-
> sdarche@efficios.com/
>
> Simon
Thanks for the pointer. Yes, it indeed would address our issue.
We can omit this patch (26/44) if the patch linked above
gets merged.
-Baris
Intel Deutschland GmbH
Registered Address: Dornacher Straße 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht München HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread
* [PATCH v3 27/44] gdbserver: import AC_LIB_HAVE_LINKFLAGS macro into the autoconf script
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (25 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 26/44] gdb: load solibs if the target does not have the notion of an exec file Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 28/44] gdbserver: add a pointer to the owner thread in regcache Tankut Baris Aktemur
` (19 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
AC_LIB_HAVE_LINKFLAGS macro is available in gdb/configure.ac but
was not in gdbserver/configure.ac. Include the same m4 files that
gdb/configure.ac includes for AC_LIB_HAVE_LINKFLAGS.
---
gdbserver/acinclude.m4 | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/gdbserver/acinclude.m4 b/gdbserver/acinclude.m4
index 5a00c6e9878bd5e3effa849685a49b15867deec0..44dae81e1387adc2fccd29ddd685d2e18f357f1a 100644
--- a/gdbserver/acinclude.m4
+++ b/gdbserver/acinclude.m4
@@ -13,6 +13,11 @@ m4_include(../gdbsupport/compiler-type.m4)
dnl This gets AM_GDB_WARNINGS.
m4_include(../gdbsupport/warning.m4)
+dnl For AC_LIB_HAVE_LINKFLAGS.
+sinclude(../../config/lib-ld.m4)
+sinclude(../../config/lib-prefix.m4)
+sinclude(../../config/lib-link.m4)
+
dnl codeset.m4 is needed for common.m4, but not for
dnl anything else in gdbserver.
m4_include(../config/codeset.m4)
--
2.34.1
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread* [PATCH v3 28/44] gdbserver: add a pointer to the owner thread in regcache
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (26 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 27/44] gdbserver: import AC_LIB_HAVE_LINKFLAGS macro into the autoconf script Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 29/44] gdbserver: wait for stopped threads in queue_stop_reply_callback Tankut Baris Aktemur
` (18 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
Add a back-link in regcache to the thread that owns the regcache.
This will help us in future patches to refer to the right thread
object without having to rely on the global current_thread pointer.
---
gdbserver/regcache.cc | 1 +
gdbserver/regcache.h | 3 +++
2 files changed, 4 insertions(+)
diff --git a/gdbserver/regcache.cc b/gdbserver/regcache.cc
index a937a34b8d03e75df3aba7ba1244036196fdc1e7..fbdcd7ac5aa262d2c5c508dab9063ce291985ce8 100644
--- a/gdbserver/regcache.cc
+++ b/gdbserver/regcache.cc
@@ -44,6 +44,7 @@ get_thread_regcache (thread_info *thread, bool fetch)
thread->set_regcache (std::make_unique<struct regcache> (proc->tdesc));
regcache = thread->regcache ();
+ regcache->thread = thread;
}
if (fetch && !regcache->registers_fetched)
diff --git a/gdbserver/regcache.h b/gdbserver/regcache.h
index 89e801f5b26b963a11ba87caa7839e427152a514..12da0738f16b2f9790d980180f51b2555bb28e63 100644
--- a/gdbserver/regcache.h
+++ b/gdbserver/regcache.h
@@ -34,6 +34,9 @@ struct regcache : public reg_buffer_common
/* The regcache's target description. */
const struct target_desc *tdesc = nullptr;
+ /* Back-link to the thread to which this regcache belongs. */
+ thread_info *thread = nullptr;
+
/* Whether the REGISTERS buffer's contents are fetched. If false,
we haven't fetched the registers from the target yet. Note that
this register cache is _not_ pass-through, unlike GDB's. Also,
--
2.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] 92+ messages in thread* [PATCH v3 29/44] gdbserver: wait for stopped threads in queue_stop_reply_callback
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (27 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 28/44] gdbserver: add a pointer to the owner thread in regcache Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 30/44] gdbserver: adjust pid after the target attaches Tankut Baris Aktemur
` (17 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
From: Markus Metzger <markus.t.metzger@intel.com>
In queue_stop_reply_callback, we check whether a thread is stopped in the
target and then assume that the thread's status is already filled in.
This would require targets to modify the thread's status rather than the
server setting it in response to a target wait call, which kind of breaks
the abstraction.
Call wait for a stopped thread and update the wait status before asserting
that it isn't TARGET_WAITKIND_IGNORE.
---
gdbserver/server.cc | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 4c12c487bcdb57baf8ea729d6e4f73e9f335e3a9..6707e27a788fb6e9de9854e9d025a2bbda38a474 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -3936,6 +3936,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] 92+ messages in thread* [PATCH v3 30/44] gdbserver: adjust pid after the target attaches
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (28 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 29/44] gdbserver: wait for stopped threads in queue_stop_reply_callback Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 31/44] gdb: do not create a thread after a process event Tankut Baris Aktemur
` (16 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
The PID argument of the attach operation may be interpreted in a
target specific way, and may not necessarily mean that the process we
attached to has PID as its process id. This is for example the case
for Intel GT targets, where the PID refers to the process id of the
host application that is using the devices. The target in that case
creates a new process for each device where the PID of the process is
the device id/ordinal. Therefore, once the target completes the
attach, we want adjust the PID value. For this, revise the meaning
of the return value of the 'attach' target op in case of success:
return the PID of the process that was attached to.
The PID argument to the 'attach' target op is of type 'int' in caller
sites. Also, the type of the PID field in ptid_t is of type 'int'.
Hence, update the parameter of 'attach' to 'int', too.
---
gdbserver/linux-low.cc | 6 +++---
gdbserver/linux-low.h | 2 +-
gdbserver/netbsd-low.cc | 2 +-
gdbserver/netbsd-low.h | 2 +-
gdbserver/server.cc | 18 ++++++++++++++----
gdbserver/target.h | 11 ++++++++---
gdbserver/win32-low.cc | 4 ++--
gdbserver/win32-low.h | 2 +-
8 files changed, 31 insertions(+), 16 deletions(-)
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index 39642705b0d8bc614e7da84f0d0e1c34cd045b12..073289af7c94cd486908863ed91d4e1ead1920f4 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -1155,7 +1155,7 @@ static void async_file_mark (void);
of its threads. */
int
-linux_process_target::attach (unsigned long pid)
+linux_process_target::attach (int pid)
{
struct process_info *proc;
thread_info *initial_thread;
@@ -1174,7 +1174,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);
@@ -1238,7 +1238,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 e1c88ee0bb2cb64f7f1400f926e45e901d977333..d02f6745cea5c05eb35c78fed1ebae912667e703 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 2984b5b6e5649d01ce686fc5cdd96660a21b5fb6..14f87c1004f80522414fe0a300fb4416dd891eb9 100644
--- a/gdbserver/netbsd-low.cc
+++ b/gdbserver/netbsd-low.cc
@@ -105,7 +105,7 @@ netbsd_process_target::post_create_inferior ()
/* Implement the attach target_ops method. */
int
-netbsd_process_target::attach (unsigned long pid)
+netbsd_process_target::attach (int pid)
{
/* Unimplemented. */
return -1;
diff --git a/gdbserver/netbsd-low.h b/gdbserver/netbsd-low.h
index a503afd870a898e6279549e22796c7aa5a69cbaa..232ada9b9f53017ebb444138b083119cc4749b5d 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 6707e27a788fb6e9de9854e9d025a2bbda38a474..119d87cc21ca95563cf89356b2fa4fce614b5e17 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -303,16 +303,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 a98dd33676dc2156f94a08f93177ace3a0d5153d..9fa405d8538a535522cd05853f190357a0919b7e 100644
--- a/gdbserver/target.h
+++ b/gdbserver/target.h
@@ -95,9 +95,14 @@ class process_stratum_target
PID is the process ID to attach to, specified by the user
or a higher layer.
- Returns -1 if attaching is unsupported, 0 on success, and calls
- error() otherwise. */
- virtual int attach (unsigned long pid) = 0;
+ If attaching is unsupported, returns -1.
+
+ If successful, returns the ID of the process that was attached to.
+ This return value may be different from the argument PID, depending
+ on how the target interpreted the argument.
+
+ Calls error() in other cases. */
+ virtual int attach (int pid) = 0;
/* Kill process PROC. Return -1 on failure, and 0 on success. */
virtual int kill (process_info *proc) = 0;
diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc
index 89831de9d4301ae906d765cccccd4135d858ff42..cf4341f7817359b4ccd09da2ebfe4fb67c3ca11d 100644
--- a/gdbserver/win32-low.cc
+++ b/gdbserver/win32-low.cc
@@ -581,7 +581,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;
@@ -596,7 +596,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 a76ed9fad3f8e6a9d7b752b83dd77b3448d21076..fdf23c94095ea9a1896c9cd0c70e06771ab39f1c 100644
--- a/gdbserver/win32-low.h
+++ b/gdbserver/win32-low.h
@@ -103,7 +103,7 @@ class win32_process_target : public process_stratum_target
int create_inferior (const char *program,
const std::string &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] 92+ messages in thread* [PATCH v3 31/44] gdb: do not create a thread after a process event.
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (29 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 30/44] gdbserver: adjust pid after the target attaches Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 32/44] gdb, ze: on a whole process stop, mark all threads as not_resumed Tankut Baris Aktemur
` (15 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
From: Natalia Saiapova <natalia.saiapova@intel.com>
When an event occurs and GDB cannot find a thread which corresponds to
the event ptid, it creates a new thread. This patch adds a check that
if the eventing PTID is PID we skip creating the new thread. Instead try
to find a corresponding inferior and take its first non exited thread for
the event context. That prevents creating dummy threads.
---
gdb/infrun.c | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/gdb/infrun.c b/gdb/infrun.c
index ae0575c60cd087a222e30834b4aa8c317a271afe..9c7d5d6206f114078ee9363249939e9ee8918614 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -6207,7 +6207,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] 92+ messages in thread* [PATCH v3 32/44] gdb, ze: on a whole process stop, mark all threads as not_resumed
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (30 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 31/44] gdb: do not create a thread after a process event Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 33/44] gdb, dwarf, ze: add DW_OP_INTEL_regval_bits Tankut Baris Aktemur
` (14 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
From: Klaus Gerlicher <klaus.gerlicher@intel.com>
In non-stop mode, when gdbserver sends a unavailable notification via a
library load stop-reply, set all non-exited threads to non_resumed
state in case it's a whole process stop-reply.
---
gdb/remote.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/gdb/remote.c b/gdb/remote.c
index e8d3b0caff42bd8add486e3546b3275bc578efb4..7f286e56e5fdf4be8bfb67827eb9b0c7b37e196e 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -8670,11 +8670,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] 92+ messages in thread* [PATCH v3 33/44] gdb, dwarf, ze: add DW_OP_INTEL_regval_bits
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (31 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 32/44] gdb, ze: on a whole process stop, mark all threads as not_resumed Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 12:02 ` Jan Beulich
2025-08-01 9:37 ` [PATCH v3 34/44] gdbserver: allow configuring for a heterogeneous target Tankut Baris Aktemur
` (13 subsequent siblings)
46 siblings, 1 reply; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger, binutils
From: Markus Metzger <markus.t.metzger@intel.com>
Add support for a new DWARF expression proposed in
https://dwarfstd.org/ShowIssue.php?issue=201007.1.
Cc: <binutils@sourceware.org>
---
binutils/dwarf.c | 6 ++++++
gdb/dwarf2/expr.c | 37 +++++++++++++++++++++++++++++++++++++
gdb/dwarf2/expr.h | 5 +++++
gdb/dwarf2/loc.c | 2 ++
include/dwarf2.def | 4 ++++
5 files changed, 54 insertions(+)
diff --git a/binutils/dwarf.c b/binutils/dwarf.c
index f4bcb67776181fc1200eae1b3a7e43906eb74eb0..3d57f625e911b17e379fe87b7a1fd419eb57ef17 100644
--- a/binutils/dwarf.c
+++ b/binutils/dwarf.c
@@ -1705,6 +1705,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 346bf7ffab60845911d5c9b0e33d794ca79ff549..45e5b5bdc9e9f602d3aef8b97b8c6c931156d83c 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -908,6 +908,29 @@ dwarf_expr_context::deref (CORE_ADDR addr, int size, struct type *type)
/* See expr.h. */
+void
+dwarf_expr_context::read_reg (gdb_byte *buf, size_t bitoffset,
+ size_t bitsize, int dwregnum)
+{
+ struct gdbarch * const gdbarch = get_frame_arch (this->m_frame);
+ const int regnum = dwarf_reg_to_regnum_or_error (gdbarch, dwregnum);
+
+ const 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,
+ gdb::make_array_view (regbuf, regsize));
+
+ const enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ copy_bitwise (buf, 0, regbuf, bitoffset, bitsize, byte_order);
+}
+
+/* See expr.h. */
+
void
dwarf_expr_context::push_dwarf_reg_entry_value (call_site_parameter_kind kind,
call_site_parameter_u kind_u,
@@ -2407,6 +2430,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 0129fb9ad7812e8104bb0e83a3269d1bf4e944ed..be2ca068071704d381d13235f81f57b29b16a50b 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -257,6 +257,11 @@ struct dwarf_expr_context
memory reads to come from the passed-in buffer. */
void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t length);
+ /* Read BITSIZE bits from the register indicated by the DWARF register
+ number DWREGNUM starting at bit BITOFFSET into BUF. */
+ void read_reg (gdb_byte *buf, size_t bitoffset, size_t bitsize,
+ int dwregnum);
+
/* Deref ADDR with size SIZE and return a value of type TYPE.
If TYPE == nullptr, defaults to this->address_type (). */
value *deref (CORE_ADDR addr, int size, struct type *type = nullptr);
diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index 37c85d8d19210a81f52ae84d4778b50b98d62f85..558d40c59bae334d36845ce21b5177bf6ccff77c 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -2116,6 +2116,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;
@@ -3372,6 +3373,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 6d6c0e0cc3917c3571ec098ffaa848ab5b381454..533c8fd9900b522c7932a63c3b30360373e64b73 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] 92+ messages in thread* Re: [PATCH v3 33/44] gdb, dwarf, ze: add DW_OP_INTEL_regval_bits
2025-08-01 9:37 ` [PATCH v3 33/44] gdb, dwarf, ze: add DW_OP_INTEL_regval_bits Tankut Baris Aktemur
@ 2025-08-01 12:02 ` Jan Beulich
2025-08-01 12:31 ` Metzger, Markus T
0 siblings, 1 reply; 92+ messages in thread
From: Jan Beulich @ 2025-08-01 12:02 UTC (permalink / raw)
To: Tankut Baris Aktemur, Markus Metzger; +Cc: gdb-patches, binutils
On 01.08.2025 11:37, Tankut Baris Aktemur wrote:
> --- 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)
You're adding this to the GNU extensions space, which doesn't seem right,
in particular given the name.
Furthermore what you link to is in state "Accepted with modification", so
it's unclear why you couldn't simply introduce DW_OP_regval_bits itself.
Jan
^ permalink raw reply [flat|nested] 92+ messages in thread
* RE: [PATCH v3 33/44] gdb, dwarf, ze: add DW_OP_INTEL_regval_bits
2025-08-01 12:02 ` Jan Beulich
@ 2025-08-01 12:31 ` Metzger, Markus T
2025-08-01 12:50 ` Jan Beulich
0 siblings, 1 reply; 92+ messages in thread
From: Metzger, Markus T @ 2025-08-01 12:31 UTC (permalink / raw)
To: Beulich, Jan; +Cc: gdb-patches, binutils, Aktemur, Tankut Baris
Hello Jan,
>On 01.08.2025 11:37, Tankut Baris Aktemur wrote:
>> --- 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)
>
>You're adding this to the GNU extensions space, which doesn't seem right,
>in particular given the name.
What would be the correct way of adding an extension? It is, as far as I know,
only generated by the Intel Graphics Compilers, so we put INTEL in the name.
Are you asking to rename it to GNU? Or are there separate extension spaces
per producer and we should declare it somewhere else?
>Furthermore what you link to is in state "Accepted with modification", so
>it's unclear why you couldn't simply introduce DW_OP_regval_bits itself.
It is accepted, but DWARF-6 is not out yet. There are currently discussions
about a proposal to put location descriptions onto the DWARF stack. If that
gets accepted, it would provide a different way of achieving the same thing,
so DW_OP_regval_bits may be removed again or may be modified.
Since it is generated by our compiler, we would still like to add it as an extension,
even if we end up generating different DWARF in a future version.
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] 92+ messages in thread
* Re: [PATCH v3 33/44] gdb, dwarf, ze: add DW_OP_INTEL_regval_bits
2025-08-01 12:31 ` Metzger, Markus T
@ 2025-08-01 12:50 ` Jan Beulich
2025-08-08 5:25 ` Metzger, Markus T
0 siblings, 1 reply; 92+ messages in thread
From: Jan Beulich @ 2025-08-01 12:50 UTC (permalink / raw)
To: Metzger, Markus T; +Cc: gdb-patches, binutils, Aktemur, Tankut Baris
On 01.08.2025 14:31, Metzger, Markus T wrote:
>> On 01.08.2025 11:37, Tankut Baris Aktemur wrote:
>>> --- 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)
>>
>> You're adding this to the GNU extensions space, which doesn't seem right,
>> in particular given the name.
>
> What would be the correct way of adding an extension? It is, as far as I know,
> only generated by the Intel Graphics Compilers, so we put INTEL in the name.
>
> Are you asking to rename it to GNU?
Possibly. Not sure where the DW_OP_GNU_* namespace is maintained (and entries
assigned), though.
> Or are there separate extension spaces
> per producer and we should declare it somewhere else?
In principle the extensions are per "environment" aiui, which may or may not
be "producer".
Recently the DW_CFA_* handling was adjusted to cope with conflicting extensions.
Likely the same is needed here. Especially if the value chosen is at risk of
conflicting with something else down the road.
Jan
^ permalink raw reply [flat|nested] 92+ messages in thread
* RE: [PATCH v3 33/44] gdb, dwarf, ze: add DW_OP_INTEL_regval_bits
2025-08-01 12:50 ` Jan Beulich
@ 2025-08-08 5:25 ` Metzger, Markus T
0 siblings, 0 replies; 92+ messages in thread
From: Metzger, Markus T @ 2025-08-08 5:25 UTC (permalink / raw)
To: Beulich, Jan, Jakub Jelinek, Pedro Alves
Cc: gdb-patches, binutils, Aktemur, Tankut Baris
Hello Jan,
Thanks for your review.
>>>> + The DW_OP_regval_bits operation extracts a value from a register. */
>>>> +DW_OP (DW_OP_INTEL_regval_bits, 0xfe)
>>>
>>> You're adding this to the GNU extensions space, which doesn't seem right,
>>> in particular given the name.
>>
>> What would be the correct way of adding an extension? It is, as far as I
>know,
>> only generated by the Intel Graphics Compilers, so we put INTEL in the
>name.
>>
>> Are you asking to rename it to GNU?
>
>Possibly. Not sure where the DW_OP_GNU_* namespace is maintained (and
>entries
>assigned), though.
In the vicinity, I see all kinds of different prefixes. Right above this one, for
example, we have DW_OP_AARCH64, DW_OP_PGI, DW_OP_HP, and then
DW_OP_GNU.
The numbers are all in the DWARF extension space, but I cannot see any pattern
as to how that gets further partitioned by GDB. I picked numbers that were still
available in the DWARF space and not used by any other extension in that file.
Jakub, Pedro, can you maybe help here?
>> Or are there separate extension spaces
>> per producer and we should declare it somewhere else?
>
>In principle the extensions are per "environment" aiui, which may or may not
>be "producer".
>
>Recently the DW_CFA_* handling was adjusted to cope with conflicting
>extensions.
>Likely the same is needed here. Especially if the value chosen is at risk of
>conflicting with something else down the road.
There are no conflicts currently.
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] 92+ messages in thread
* [PATCH v3 34/44] gdbserver: allow configuring for a heterogeneous target
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (32 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 33/44] gdb, dwarf, ze: add DW_OP_INTEL_regval_bits Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 35/44] gdbserver, ze, intelgt: introduce ze-low and intel-ze-low targets Tankut Baris Aktemur
` (12 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
gdbserver's configure.srv requires host and target systems to be the
same. This prevents building gdbserver for when a target other than
the native CPU is to be debugged, e.g. a heterogeneous system with an
Intel GPU where we want to debug the GPU.
Allow for such a combination by special-casing the Intel GPU target to
preserve the existing behavior for other cases.
---
gdbserver/configure.srv | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv
index 6281cdadc2a2b480ca583e499ceab9074aa42ce4..9a6944b6e4eb570d9905c8dd76e3ee3bb078bbf8 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -30,11 +30,18 @@ srv_linux_obj="linux-low.o nat/linux-osdata.o nat/linux-procfs.o nat/linux-ptrac
# Input is taken from the "${host}" and "${target}" variables.
-# GDBserver can only debug native programs.
+# GDBserver can only debug native programs. An exception to this is a
+# heterogeneous system, e.g. an Intel GPU.
if test "${target}" = "${host}"; then
gdbserver_host=${host}
else
- gdbserver_host=
+ case "${target}" in
+ intelgt*) gdbserver_host=${target}
+ ;;
+
+ *) gdbserver_host=
+ ;;
+ esac
fi
case "${gdbserver_host}" in
--
2.34.1
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread* [PATCH v3 35/44] gdbserver, ze, intelgt: introduce ze-low and intel-ze-low targets
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (33 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 34/44] gdbserver: allow configuring for a heterogeneous target Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 36/44] testsuite, sycl: add SYCL support Tankut Baris Aktemur
` (11 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger, config-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>
Cc: <config-patches@gnu.org>
Cc: <binutils@sourceware.org>
---
config.sub | 3 +-
gdbserver/Makefile.in | 4 +-
gdbserver/config.in | 6 +
gdbserver/configure | 500 ++++++++
gdbserver/configure.ac | 18 +
gdbserver/configure.srv | 4 +
gdbserver/gdbthread.h | 2 +-
gdbserver/intelgt-ze-low.cc | 1016 +++++++++++++++
gdbserver/ze-low.cc | 2996 +++++++++++++++++++++++++++++++++++++++++++
gdbserver/ze-low.h | 496 +++++++
10 files changed, 5042 insertions(+), 3 deletions(-)
diff --git a/config.sub b/config.sub
index 664ee26124ac817257f076ed1d95f99cd0530fa4..2a54c8a9b410e367c07c38bf061f2bd4be9f74d4 100755
--- a/config.sub
+++ b/config.sub
@@ -2149,6 +2149,7 @@ case $os in
| winnt* \
| xenix* \
| xray* \
+ | ze* \
| zephyr* \
| zvmoe* )
;;
@@ -2211,7 +2212,7 @@ esac
case $kernel-$os-$obj in
linux-gnu*- | linux-android*- | linux-dietlibc*- | linux-llvm*- \
| linux-mlibc*- | linux-musl*- | linux-newlib*- \
- | linux-relibc*- | linux-uclibc*- | linux-ohos*- )
+ | linux-relibc*- | linux-uclibc*- | linux-ohos*- | linux-ze*- )
;;
uclinux-uclibc*- | uclinux-gnu*- )
;;
diff --git a/gdbserver/Makefile.in b/gdbserver/Makefile.in
index dbcc18a85dc70f2aaa8bf71c7d6865cd7f264bd1..230a948b1c02f33b7825ff53e984806ccab7fc3f 100644
--- a/gdbserver/Makefile.in
+++ b/gdbserver/Makefile.in
@@ -104,6 +104,8 @@ LIBIBERTY = $(LIBIBERTY_NORMAL)
GDBSUPPORT_BUILDDIR = ../gdbsupport
GDBSUPPORT = $(GDBSUPPORT_BUILDDIR)/libgdbsupport.a
+LIBZE_LOADER = @LIBZE_LOADER@
+
# gnulib
GNULIB_PARENT_DIR = ..
include $(GNULIB_PARENT_DIR)/gnulib/Makefile.gnulib.inc
@@ -369,7 +371,7 @@ gdbserver$(EXEEXT): $(sort $(OBS)) ${CDEPS} $(LIBGNU) $(LIBIBERTY) \
$(CXXFLAGS) \
-o gdbserver$(EXEEXT) $(OBS) $(GDBSUPPORT) $(LIBGNU) \
$(LIBGNU_EXTRA_LIBS) $(LIBIBERTY) $(INTL) \
- $(GDBSERVER_LIBS) $(XM_CLIBS) $(WIN32APILIBS) $(MAYBE_LIBICONV)
+ $(GDBSERVER_LIBS) $(XM_CLIBS) $(WIN32APILIBS) $(MAYBE_LIBICONV) $(LIBZE_LOADER)
gdbreplay$(EXEEXT): $(sort $(GDBREPLAY_OBS)) $(LIBGNU) $(LIBIBERTY) \
$(INTL_DEPS) $(GDBSUPPORT)
diff --git a/gdbserver/config.in b/gdbserver/config.in
index ead66ce7a5746c5820f06e985332e4df9949b0ba..e4ba29ecd5d3d5f7993d69289bc70bcffb88f6a4 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 b45b55ffde7a23716fd9f788bc7eeecc183e96d6..828105595ca065d291fecdf274ead12b11e1c58a 100755
--- a/gdbserver/configure
+++ b/gdbserver/configure
@@ -631,6 +631,9 @@ srv_xmlfiles
srv_xmlbuiltin
GDBSERVER_LIBS
GDBSERVER_DEPFILES
+LTLIBZE_LOADER
+LIBZE_LOADER
+HAVE_LIBZE_LOADER
RDYNAMIC
REPORT_BUGS_TEXI
REPORT_BUGS_TO
@@ -780,6 +783,8 @@ with_pkgversion
with_bugurl
with_libthread_db
enable_inprocess_agent
+with_libze_loader_prefix
+with_libze_loader_type
'
ac_precious_vars='build_alias
host_alias
@@ -1450,6 +1455,9 @@ Optional Packages:
--with-bugurl=URL Direct users to URL to report a bug
--with-libthread-db=PATH
use given libthread_db directly
+ --with-libze_loader-prefix[=DIR] search for libze_loader in DIR/include and DIR/lib
+ --without-libze_loader-prefix don't search for libze_loader in includedir and libdir
+ --with-libze_loader-type=TYPE type of library to search for (auto/static/shared)
Some influential environment variables:
CC C compiler command
@@ -14725,6 +14733,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 bd2cac8d35db87915b7c20e5333b191caf7a485f..ce07a4d470598bf3623435c2b8f71478e884f997 100644
--- a/gdbserver/configure.ac
+++ b/gdbserver/configure.ac
@@ -403,6 +403,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 9a6944b6e4eb570d9905c8dd76e3ee3bb078bbf8..1c54bb8b992003726c34fc1db3f89b3df3a0dde4 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -147,6 +147,10 @@ case "${gdbserver_host}" in
srv_tgtobj="$srv_linux_obj linux-ia64-low.o"
srv_linux_usrregs=yes
;;
+ intelgt-*-ze) srv_regobj=""
+ srv_xmlfiles=""
+ srv_tgtobj="ze-low.o intelgt-ze-low.o arch/intelgt.o"
+ ;;
loongarch*-*-linux*) srv_tgtobj="arch/loongarch.o linux-loongarch-low.o"
srv_tgtobj="${srv_tgtobj} ${srv_linux_obj}"
srv_tgtobj="$srv_tgtobj nat/loongarch-hw-point.o"
diff --git a/gdbserver/gdbthread.h b/gdbserver/gdbthread.h
index 5e6130106f5f44a275f9e9873c72b8bfc0cba4d8..7f280843c301baa27d8c30709ff8a75a039f63e9 100644
--- a/gdbserver/gdbthread.h
+++ b/gdbserver/gdbthread.h
@@ -42,7 +42,7 @@ struct thread_info : public intrusive_list_node<thread_info>
void set_regcache (std::unique_ptr<struct regcache> regcache)
{ m_regcache = std::move (regcache); }
- void *target_data ()
+ void *target_data () const
{ return m_target_data; }
/* The id of this thread. */
diff --git a/gdbserver/intelgt-ze-low.cc b/gdbserver/intelgt-ze-low.cc
new file mode 100644
index 0000000000000000000000000000000000000000..ac0c157a46a778f6014f61192b3bd6fa7c8c94dc
--- /dev/null
+++ b/gdbserver/intelgt-ze-low.cc
@@ -0,0 +1,1016 @@
+/* Target interface for Intel GT based on Level-Zero for gdbserver.
+
+ Copyright (C) 2020-2025 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "ze-low.h"
+#include "arch/intelgt.h"
+#include "gdbsupport/osabi.h"
+
+#include <level_zero/zet_intel_gpu_debug.h>
+#include <iomanip>
+#include <sstream>
+
+
+/* FIXME make into a target method? */
+int using_threads = 1;
+
+/* Convenience macros. */
+
+#define dprintf(...) \
+ do \
+ { \
+ if (debug_threads) \
+ { \
+ debug_printf ("%s: ", __FUNCTION__); \
+ debug_printf (__VA_ARGS__); \
+ debug_printf ("\n"); \
+ } \
+ } \
+ while (0)
+
+
+/* Determine the most suitable type to be used for a register with bit size
+ BITSIZE and element size ELEMSIZE. */
+
+static const char *
+intelgt_uint_reg_type (tdesc_feature *feature, uint32_t bitsize,
+ uint32_t elemsize)
+{
+ if (0 != (bitsize % elemsize))
+ error (_("unsupported combination of bitsize %" PRIu32 "and elemsize %"
+ PRIu32), bitsize, elemsize);
+ if ((elemsize < 8) || (elemsize > 128) || ((elemsize & (elemsize - 1)) != 0))
+ error (_("unsupported elemsize %" PRIu32), elemsize);
+
+ char type_name[20];
+ snprintf (type_name, sizeof (type_name), "uint%u", elemsize);
+ tdesc_type *type = tdesc_named_type (feature, type_name);
+
+ if (elemsize == bitsize)
+ return type->name.c_str ();
+
+ uint32_t elements = bitsize / elemsize;
+ snprintf (type_name, sizeof (type_name), "vector%ux%u", elements,
+ elemsize);
+ tdesc_type *vector
+ = tdesc_create_vector (feature, type_name, type, elements);
+
+ return vector->name.c_str ();
+}
+
+/* Add a (uniform) register set to FEATURE. */
+
+static void
+intelgt_add_regset (tdesc_feature *feature, long ®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 ();
+}
+
+static uint32_t
+get_device_id (ze_device_info *device)
+{
+ gdb_assert (device != nullptr);
+ return device->properties.deviceId;
+}
+
+/* Target op definitions for Intel GT target based on Level-Zero. */
+
+class intelgt_ze_target : public ze_target
+{
+public:
+ const gdb_byte *sw_breakpoint_from_kind (int kind, int *size) override;
+
+ bool supports_stopped_by_sw_breakpoint () override { return true; }
+ bool stopped_by_sw_breakpoint () override;
+
+ CORE_ADDR read_pc (regcache *regcache) override;
+ void write_pc (regcache *regcache, CORE_ADDR pc) override;
+
+protected:
+ bool is_device_supported
+ (const ze_device_properties_t &,
+ const std::vector<zet_debug_regset_properties_t> &) override;
+
+ target_desc *create_tdesc
+ (ze_device_info *dinfo,
+ const std::vector<zet_debug_regset_properties_t> &,
+ const ze_pci_ext_properties_t &) override;
+
+ target_stop_reason get_stop_reason (thread_info *, gdb_signal &) override;
+
+ void prepare_thread_resume (thread_info *tp) override;
+
+ /* Read one instruction from memory at PC into BUFFER and return the
+ number of bytes read on success or a negative errno error code.
+
+ BUFFER must be intelgt::MAX_INST_LENGTH bytes long. */
+ int read_inst (thread_info *tp, CORE_ADDR pc,
+ gdb::array_view<gdb_byte> buffer);
+
+ bool is_at_breakpoint (thread_info *tp) override;
+ bool is_at_eot (thread_info *tp);
+
+ bool erratum_18020355813 (thread_info *tp);
+
+private:
+ /* Add a register set for REGPROP on DEVICE to REGSETS and increment REGNUM
+ accordingly.
+
+ May optionally add registers to EXPEDITE. */
+ void add_regset (target_desc *tdesc, const ze_device_info &dinfo,
+ 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 = current_thread->id;
+
+ if (zetp->exec_state != ZE_THREAD_STATE_STOPPED)
+ {
+ dprintf ("not-stopped thread %s", ptid.to_string ().c_str ());
+ return false;
+ }
+
+ return (zetp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT);
+}
+
+CORE_ADDR
+intelgt_ze_target::read_pc (regcache *regcache)
+{
+ uint32_t ip = intelgt_read_cr0 (regcache, 2);
+ uint64_t isabase;
+ collect_register_by_name (regcache, "isabase", &isabase);
+
+ if (UINT32_MAX < ip)
+ warning (_("IP '0x%" PRIx32 "' outside of ISA range."), ip);
+
+ CORE_ADDR pc = (CORE_ADDR) isabase + (CORE_ADDR) ip;
+ if (pc < isabase)
+ warning (_("PC '%s' outside of ISA range."),
+ core_addr_to_string_nz (pc));
+
+ return pc;
+}
+
+void
+intelgt_ze_target::write_pc (regcache *regcache, CORE_ADDR pc)
+{
+ uint64_t isabase;
+ collect_register_by_name (regcache, "isabase", &isabase);
+
+ if (pc < isabase)
+ error (_("PC '%s' outside of ISA range."), core_addr_to_string_nz (pc));
+
+ pc -= isabase;
+ if (UINT32_MAX < pc)
+ error (_("PC '%s' outside of ISA range."), core_addr_to_string_nz (pc));
+
+ intelgt_write_cr0 (regcache, 2, (uint32_t) pc);
+}
+
+bool
+intelgt_ze_target::is_device_supported
+ (const ze_device_properties_t &properties,
+ const std::vector<zet_debug_regset_properties_t> ®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 (), GDB_OSABI_LINUX);
+
+ std::string device_uuid = device_uuid_str (
+ dinfo->properties.uuid.id, sizeof (dinfo->properties.uuid.id));
+ const uint32_t total_cores = (properties.numSlices
+ * properties.numSubslicesPerSlice
+ * properties.numEUsPerSubslice);
+ const uint32_t total_threads = (total_cores * properties.numThreadsPerEU);
+
+ tdesc_device *device_info = new tdesc_device ();
+ device_info->vendor_id = properties.vendorId;
+ device_info->target_id = properties.deviceId;
+ device_info->name = properties.name;
+ device_info->pci_slot = string_printf ("%02" PRIx32 ":%02" PRIx32
+ ".%" PRId32,
+ pci_properties.address.bus,
+ pci_properties.address.device,
+ pci_properties.address.function);
+ device_info->uuid = device_uuid;
+ device_info->total_cores = total_cores;
+ device_info->total_threads = total_threads;
+
+ if (properties.flags & ZE_DEVICE_PROPERTY_FLAG_SUBDEVICE)
+ device_info->subdevice_id = properties.subdeviceId;
+
+ set_tdesc_device_info (tdesc.get (), device_info);
+
+ long regnum = 0;
+ for (const zet_debug_regset_properties_t ®prop : regset_properties)
+ add_regset (tdesc.get (), *dinfo, regprop, regnum,
+ dinfo->regsets, dinfo->expedite);
+
+ /* Tdesc expects a nullptr-terminated array. */
+ dinfo->expedite.push_back (nullptr);
+
+ init_target_desc (tdesc.get (), dinfo->expedite.data (), GDB_OSABI_LINUX);
+ return tdesc.release ();
+}
+
+target_stop_reason
+intelgt_ze_target::get_stop_reason (thread_info *tp, gdb_signal &signal)
+{
+ ze_device_thread_t thread = ze_thread_id (tp);
+ uint32_t cr0[3] = {
+ intelgt_read_cr0 (tp, 0),
+ intelgt_read_cr0 (tp, 1),
+ intelgt_read_cr0 (tp, 2)
+ };
+
+ dprintf ("thread %s (%s) stopped, cr0.0=%" PRIx32 ", .1=%" PRIx32
+ " [ %s%s%s%s%s%s], .2=%" PRIx32 ".", tp->id.to_string ().c_str (),
+ ze_thread_id_str (thread).c_str (), cr0[0], cr0[1],
+ (((cr0[1] & (1 << INTELGT_CR0_1_BREAKPOINT_STATUS)) != 0)
+ ? "bp " : ""),
+ (((cr0[1] & (1 << INTELGT_CR0_1_ILLEGAL_OPCODE_STATUS)) != 0)
+ ? "ill " : ""),
+ (((cr0[1] & (1 << INTELGT_CR0_1_FORCE_EXCEPTION_STATUS)) != 0)
+ ? "fe " : ""),
+ (((cr0[1] & (1 << INTELGT_CR0_1_SOFTWARE_EXCEPTION_CONTROL)) != 0)
+ ? "sw " : ""),
+ (((cr0[1] & (1 << INTELGT_CR0_1_EXTERNAL_HALT_STATUS)) != 0)
+ ? "eh " : ""),
+ (((cr0[1] & (1 << INTELGT_CR0_1_PAGEFAULT_STATUS)) != 0)
+ ? "pf " : ""),
+ cr0[2]);
+
+ if ((cr0[1] & (1 << INTELGT_CR0_1_PAGEFAULT_STATUS)) != 0)
+ {
+ cr0[1] &= ~(1 << INTELGT_CR0_1_PAGEFAULT_STATUS);
+ intelgt_write_cr0 (tp, 1, cr0[1]);
+
+ signal = GDB_SIGNAL_SEGV;
+ return TARGET_STOPPED_BY_NO_REASON;
+ }
+
+ if ((cr0[1] & (1 << INTELGT_CR0_1_BREAKPOINT_STATUS)) != 0)
+ {
+ cr0[1] &= ~(1 << INTELGT_CR0_1_BREAKPOINT_STATUS);
+ intelgt_write_cr0 (tp, 1, cr0[1]);
+
+ /* We cannot distinguish a single step exception from a breakpoint
+ exception just by looking at CR0.
+
+ We could inspect the instruction to see if the breakpoint bit is
+ set. Or we could check the resume type and assume that we set
+ things up correctly for single-stepping before we resumed. */
+ const ze_thread_info *zetp = ze_thread (tp);
+ gdb_assert (zetp != nullptr);
+
+ switch (zetp->resume_state)
+ {
+ case ZE_THREAD_RESUME_STEP:
+ signal = GDB_SIGNAL_TRAP;
+ return TARGET_STOPPED_BY_SINGLE_STEP;
+
+ case ZE_THREAD_RESUME_RUN:
+ case ZE_THREAD_RESUME_NONE:
+ /* On some devices, we may get spurious breakpoint exceptions. */
+ if (erratum_18020355813 (tp))
+ {
+ ze_device_thread_t zeid = ze_thread_id (tp);
+
+ dprintf ("applying #18020355813 workaround for thread "
+ "%s (%s)", tp->id.to_string ().c_str (),
+ ze_thread_id_str (zeid).c_str ());
+
+ signal = GDB_SIGNAL_0;
+ return TARGET_STOPPED_BY_NO_REASON;
+ }
+
+ [[fallthrough]];
+ case ZE_THREAD_RESUME_STOP:
+ signal = GDB_SIGNAL_TRAP;
+ return TARGET_STOPPED_BY_SW_BREAKPOINT;
+ }
+ }
+
+ if ((cr0[1] & (1 << INTELGT_CR0_1_ILLEGAL_OPCODE_STATUS)) != 0)
+ {
+ cr0[1] &= ~(1 << INTELGT_CR0_1_ILLEGAL_OPCODE_STATUS);
+ intelgt_write_cr0 (tp, 1, cr0[1]);
+
+ signal = GDB_SIGNAL_ILL;
+ return TARGET_STOPPED_BY_NO_REASON;
+ }
+
+ if ((cr0[1] & (1 << INTELGT_CR0_1_SOFTWARE_EXCEPTION_CONTROL)) != 0)
+ {
+ cr0[1] &= ~(1 << INTELGT_CR0_1_SOFTWARE_EXCEPTION_CONTROL);
+ intelgt_write_cr0 (tp, 1, cr0[1]);
+
+ signal = GDB_EXC_SOFTWARE;
+ return TARGET_STOPPED_BY_NO_REASON;
+ }
+
+ if ((cr0[1] & ((1 << INTELGT_CR0_1_FORCE_EXCEPTION_STATUS)
+ | (1 << INTELGT_CR0_1_EXTERNAL_HALT_STATUS))) != 0)
+ {
+ cr0[1] &= ~(1 << INTELGT_CR0_1_FORCE_EXCEPTION_STATUS);
+ cr0[1] &= ~(1 << INTELGT_CR0_1_EXTERNAL_HALT_STATUS);
+ intelgt_write_cr0 (tp, 1, cr0[1]);
+
+ signal = GDB_SIGNAL_TRAP;
+ return TARGET_STOPPED_BY_NO_REASON;
+ }
+
+ signal = GDB_SIGNAL_UNKNOWN;
+ return TARGET_STOPPED_BY_NO_REASON;
+}
+
+int
+intelgt_ze_target::read_inst (thread_info *tp, CORE_ADDR pc,
+ gdb::array_view<gdb_byte> buffer)
+{
+ gdb_assert (buffer.size () >= intelgt::MAX_INST_LENGTH);
+
+ int status = read_memory (tp, pc, buffer.data (), intelgt::MAX_INST_LENGTH);
+ if (status == 0)
+ return intelgt::MAX_INST_LENGTH;
+
+ status = read_memory (tp, pc, buffer.data (), intelgt::COMPACT_INST_LENGTH);
+ if (status > 0)
+ return status;
+
+ uint32_t device_id = get_device_id (ze_thread_device (tp));
+ if (intelgt::inst_length (buffer, device_id) == intelgt::MAX_INST_LENGTH)
+ return -EIO;
+
+ memset (buffer.begin () + intelgt::COMPACT_INST_LENGTH, 0,
+ intelgt::MAX_INST_LENGTH - intelgt::COMPACT_INST_LENGTH);
+
+ return intelgt::COMPACT_INST_LENGTH;
+}
+
+bool
+intelgt_ze_target::is_at_breakpoint (thread_info *tp)
+{
+ regcache *regcache = get_thread_regcache (tp, /* fetch = */ true);
+ CORE_ADDR pc = read_pc (regcache);
+
+ gdb_byte inst[intelgt::MAX_INST_LENGTH];
+ int status = read_inst (tp, pc, inst);
+ if (status < 0)
+ return false;
+
+ uint32_t device_id = get_device_id (ze_thread_device (tp));
+ return intelgt::has_breakpoint (inst, device_id);
+}
+
+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;
+ }
+
+ uint32_t device_id = get_device_id (ze_thread_device (tp));
+ intelgt::xe_version device_version = intelgt::get_xe_version (device_id);
+ switch (device_version)
+ {
+ case intelgt::XE_HP:
+ case intelgt::XE_HPG:
+ case intelgt::XE_HPC:
+ case intelgt::XE2:
+ case intelgt::XE3:
+ {
+ /* The opcode mask for bits 6:0. */
+ constexpr uint8_t OPC_MASK = 0x7f;
+ switch (inst[0] & OPC_MASK)
+ {
+ case 0x31: /* send */
+ case 0x32: /* sendc */
+ {
+ /* The End Of Thread control. Only used for SEND and
+ SENDC. */
+ constexpr uint8_t CTRL_EOT_SEND = 34;
+ return intelgt::get_inst_bit (inst, CTRL_EOT_SEND);
+ }
+
+ default:
+ return false;
+ }
+ }
+
+ default:
+ error (_("Unsupported device id 0x%" PRIx32), device_id);
+ }
+}
+
+/* Return whether erratum #18020355813 applies. */
+
+bool
+intelgt_ze_target::erratum_18020355813 (thread_info *tp)
+{
+ ze_device_info *device = ze_thread_device (tp);
+
+ /* We may not have a device if we got detached. */
+ if (device == nullptr)
+ return false;
+
+ /* The erratum only applies to Intel devices. */
+ if (device->properties.vendorId != 0x8086)
+ return false;
+
+ uint32_t device_id = get_device_id (device);
+
+ /* The erratum only applies to a range of devices. */
+ switch (intelgt::get_xe_version (device_id))
+ {
+ case intelgt::XE_HPG:
+ case intelgt::XE_HPC:
+ 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, device_id);
+}
+
+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[0] |= (1 << INTELGT_CR0_0_BREAKPOINT_SUPPRESS);
+ 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_info &dinfo,
+ const zet_debug_regset_properties_t ®prop,
+ long ®num, ze_regset_info_t ®sets,
+ expedite_t &expedite)
+{
+ tdesc_feature *feature = nullptr;
+ const ze_device_properties_t &device = dinfo.properties;
+
+ ze_regset_info regset = {};
+ regset.type = (uint32_t) regprop.type;
+ regset.size = regprop.byteSize;
+ regset.begin = regnum;
+ regset.is_writeable
+ = ((regprop.generalFlags & ZET_DEBUG_REGSET_FLAG_WRITEABLE) != 0);
+
+ if (regprop.count < 1)
+ {
+ warning (_("Ignoring empty regset %u in %s."), regprop.type,
+ device.name);
+ return;
+ }
+
+ if ((regprop.generalFlags & ZET_DEBUG_REGSET_FLAG_READABLE) == 0)
+ {
+ warning (_("Ignoring non-readable regset %u in %s."), regprop.type,
+ device.name);
+ return;
+ }
+
+ switch (regprop.type)
+ {
+ case ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU:
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_GRF);
+
+ expedite.push_back ("r0");
+
+ intelgt_add_regset (feature, regnum, "r", regprop.count, "grf",
+ regprop.bitSize,
+ intelgt_uint_reg_type (feature, regprop.bitSize,
+ 32u),
+ expedite);
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_ADDR_INTEL_GPU:
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_ADDR);
+
+ intelgt_add_regset (feature, regnum, "a", regprop.count, "arf",
+ regprop.bitSize,
+ intelgt_uint_reg_type (feature, regprop.bitSize,
+ 16u),
+ expedite);
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_FLAG_INTEL_GPU:
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_FLAG);
+
+ intelgt_add_regset (feature, regnum, "f", regprop.count, "arf",
+ regprop.bitSize,
+ intelgt_uint_reg_type (feature, regprop.bitSize,
+ 16u),
+ expedite);
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_CE_INTEL_GPU:
+ /* We expect a single 'ce' register. */
+ if (regprop.count != 1)
+ warning (_("Ignoring %u unexpected 'ce' registers in %s."),
+ regprop.count - 1, device.name);
+
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_CE);
+
+ tdesc_create_reg (feature, "ce", regnum++, 1, "arf",
+ regprop.bitSize,
+ intelgt_uint_reg_type (feature, regprop.bitSize,
+ 32u));
+
+ expedite.push_back ("ce");
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_SR_INTEL_GPU:
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_SR);
+
+ expedite.push_back ("sr0");
+ intelgt_add_regset (feature, regnum, "sr", regprop.count, "arf",
+ regprop.bitSize,
+ intelgt_uint_reg_type (feature, regprop.bitSize,
+ 32u),
+ expedite);
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_CR_INTEL_GPU:
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_CR);
+
+ expedite.push_back ("cr0");
+ intelgt_add_regset (feature, regnum, "cr", regprop.count, "arf",
+ regprop.bitSize,
+ intelgt_uint_reg_type (feature, regprop.bitSize,
+ 32u),
+ expedite);
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_TDR_INTEL_GPU:
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_TDR);
+
+ intelgt_add_regset (feature, regnum, "tdr", regprop.count, "arf",
+ regprop.bitSize,
+ intelgt_uint_reg_type (feature, regprop.bitSize,
+ 16u),
+ expedite);
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_ACC_INTEL_GPU:
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_ACC);
+
+ intelgt_add_regset (feature, regnum, "acc", regprop.count, "arf",
+ regprop.bitSize,
+ intelgt_uint_reg_type (feature, regprop.bitSize,
+ 32u),
+ expedite);
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_MME_INTEL_GPU:
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_MME);
+
+ intelgt_add_regset (feature, regnum, "mme", regprop.count, "arf",
+ regprop.bitSize,
+ intelgt_uint_reg_type (feature, regprop.bitSize,
+ 32u),
+ expedite);
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_SP_INTEL_GPU:
+ /* We expect a single 'sp' register. */
+ if (regprop.count != 1)
+ warning (_("Ignoring %u unexpected 'sp' registers in %s."),
+ regprop.count - 1, device.name);
+
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_SP);
+
+ tdesc_create_reg (feature, "sp", regnum++, 1, "arf",
+ regprop.bitSize,
+ intelgt_uint_reg_type (feature, regprop.bitSize,
+ regprop.bitSize));
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_SBA_INTEL_GPU:
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_SBA);
+
+ switch (regprop.version)
+ {
+ case 0:
+ {
+ const char *regtype = intelgt_uint_reg_type (feature,
+ regprop.bitSize,
+ regprop.bitSize);
+ const char *sbaregs[] = {
+ "genstbase",
+ "sustbase",
+ "dynbase",
+ "iobase",
+ "isabase",
+ "blsustbase",
+ "blsastbase",
+ "btbase",
+ "scrbase",
+ "scrbase2",
+ nullptr
+ };
+ int reg = 0;
+ for (; (reg < regprop.count) && (sbaregs[reg] != nullptr); ++reg)
+ {
+ if ((strcmp (sbaregs[reg], "genstbase") == 0)
+ || (strcmp (sbaregs[reg], "isabase") == 0))
+ {
+ expedite.push_back (sbaregs[reg]);
+ }
+
+ tdesc_create_reg (feature, sbaregs[reg], regnum++, 1,
+ "virtual", regprop.bitSize, regtype);
+ }
+ }
+ break;
+
+ default:
+ warning (_("Ignoring unknown SBA regset version %u in %s"),
+ regprop.version, device.name);
+ break;
+ }
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_DBG_INTEL_GPU:
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_DBG);
+
+ intelgt_add_regset (feature, regnum, "dbg", regprop.count, "arf",
+ regprop.bitSize,
+ intelgt_uint_reg_type (feature, regprop.bitSize,
+ 32u),
+ expedite);
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_FC_INTEL_GPU:
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_FC);
+
+ intelgt_add_regset (feature, regnum, "fc", regprop.count, "arf",
+ regprop.bitSize,
+ intelgt_uint_reg_type (feature, regprop.bitSize,
+ 32u),
+ expedite);
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_INVALID_INTEL_GPU:
+ case ZET_DEBUG_REGSET_TYPE_FORCE_UINT32:
+ break;
+ }
+
+ if (feature == nullptr)
+ {
+ warning (_("Ignoring unknown regset %u in %s."), regprop.type,
+ device.name);
+
+ return;
+ }
+
+ regset.end = regnum;
+ regsets.push_back (regset);
+}
+
+
+/* The Intel GT target ops object. */
+
+static intelgt_ze_target the_intelgt_ze_target;
+
+extern void initialize_low ();
+void
+initialize_low ()
+{
+ /* Delayed initialization of Level-Zero targets. See ze-low.h. */
+ the_intelgt_ze_target.init ();
+ set_target_ops (&the_intelgt_ze_target);
+}
diff --git a/gdbserver/ze-low.cc b/gdbserver/ze-low.cc
new file mode 100644
index 0000000000000000000000000000000000000000..d3dfb1f175ba8b406a9f9d927c71ca6ea5154d2e
--- /dev/null
+++ b/gdbserver/ze-low.cc
@@ -0,0 +1,2996 @@
+/* Target interface for Level-Zero based targets for gdbserver.
+ See https://github.com/oneapi-src/level-zero.git.
+
+ Copyright (C) 2020-2025 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "ze-low.h"
+#include "dll.h"
+
+#include <level_zero/zet_api.h>
+#include <exception>
+#include <sstream>
+#include <iomanip>
+#include <cstring> /* For snprintf. */
+#include <thread>
+#include <utility>
+#include <algorithm>
+#include <set>
+
+#ifndef USE_WIN32API
+# include <signal.h>
+# include <fcntl.h>
+#endif
+
+
+/* Convenience macros. */
+
+#define dprintf(...) \
+ do \
+ { \
+ if (debug_threads) \
+ { \
+ debug_printf ("%s: ", __FUNCTION__); \
+ debug_printf (__VA_ARGS__); \
+ debug_printf ("\n"); \
+ } \
+ } \
+ while (0)
+
+#ifndef USE_WIN32API
+/* Async interaction stuff.
+
+ The read/write ends of the pipe registered as waitable file in the
+ event loop. */
+static int ze_event_pipe[2] = { -1, -1 };
+#endif
+
+/* Return whether we're in async mode. */
+
+static bool
+ze_is_async ()
+{
+#ifndef USE_WIN32API
+ return (ze_event_pipe[0] != -1);
+#else
+ return false;
+#endif
+}
+
+/* Get rid of any pending event in the pipe. */
+
+static void
+ze_async_flush ()
+{
+ if (!ze_is_async ())
+ return;
+
+#ifndef USE_WIN32API
+ int ret;
+ char buf;
+
+ errno = 0;
+ do
+ ret = read (ze_event_pipe[0], &buf, 1);
+ while (ret >= 0 || (ret == -1 && errno == EINTR));
+#else
+ error (_("%s: tbd"), __FUNCTION__);
+#endif
+}
+
+/* Put something in the pipe, so the event loop wakes up. */
+
+static void
+ze_async_mark ()
+{
+ if (!ze_is_async ())
+ return;
+
+#ifndef USE_WIN32API
+ int ret;
+
+ ze_async_flush ();
+
+ errno = 0;
+ do
+ ret = write (ze_event_pipe[1], "+", 1);
+ while (ret == 0 || (ret == -1 && errno == EINTR));
+
+ /* Ignore EAGAIN. If the pipe is full, the event loop will already
+ be awakened anyway. */
+#else
+ error (_("%s: tbd"), __FUNCTION__);
+#endif
+}
+
+/* Return a human-readable device thread id component string. */
+
+static std::string
+ze_thread_id_component_str (uint32_t component)
+{
+ if (component == UINT32_MAX)
+ return std::string ("all");
+
+ return std::to_string (component);
+}
+
+/* See ze-low.h. */
+
+std::string
+ze_thread_id_str (const ze_device_thread_t &thread)
+{
+ std::stringstream sstream;
+ sstream << ze_thread_id_component_str (thread.slice)
+ << "."
+ << ze_thread_id_component_str (thread.subslice)
+ << "."
+ << ze_thread_id_component_str (thread.eu)
+ << "."
+ << ze_thread_id_component_str (thread.thread);
+
+ return sstream.str ();
+}
+
+/* Return a human-readable UUID string. */
+
+static std::string
+uuid_str (const uint8_t uuid[], size_t size)
+{
+ std::stringstream sstream;
+ while (size--)
+ sstream << std::setw (2) << uuid[size];
+
+ return sstream.str ();
+}
+
+/* Return a human-readable device UUID string. */
+
+static std::string
+driver_uuid_str (const ze_driver_uuid_t &uuid)
+{
+ return uuid_str (uuid.id, sizeof (uuid.id));
+}
+
+/* Return a human-readable process state string. */
+
+static const char *
+ze_process_state_str (ze_process_state state)
+{
+ switch (state)
+ {
+ case ZE_PROCESS_VISIBLE:
+ return "visible";
+
+ case ZE_PROCESS_HIDDEN:
+ return "hidden";
+ }
+
+ return "unknown";
+}
+
+/* Return the pid for DEVICE. */
+
+static int
+ze_device_pid (const ze_device_info &device)
+{
+ if (device.process != nullptr)
+ return device.process->pid;
+
+ return 0;
+}
+
+/* Return the device for PROCESS. */
+
+static ze_device_info *
+ze_process_device (process_info *process)
+{
+ if (process == nullptr)
+ return nullptr;
+
+ process_info_private *zeproc = process->priv;
+ if (zeproc == nullptr)
+ return nullptr;
+
+ return zeproc->device;
+}
+
+/* Return the device for THREAD. */
+
+ze_device_info *
+ze_thread_device (const thread_info *thread)
+{
+ if (thread == nullptr)
+ return nullptr;
+
+ return ze_process_device (thread->process ());
+}
+
+/* Returns whether ID is in SET. */
+
+static bool
+ze_device_thread_in (ze_device_thread_t id, ze_device_thread_t set)
+{
+ if ((set.slice != UINT32_MAX) && (set.slice != id.slice))
+ return false;
+
+ if ((set.subslice != UINT32_MAX) && (set.subslice != id.subslice))
+ return false;
+
+ if ((set.eu != UINT32_MAX) && (set.eu != id.eu))
+ return false;
+
+ if ((set.thread != UINT32_MAX) && (set.thread != id.thread))
+ return false;
+
+ return true;
+}
+
+/* Call FUNC for each thread on DEVICE matching ID. */
+
+template <typename Func>
+static void
+for_each_thread (const ze_device_info &device, ze_device_thread_t id,
+ Func func)
+{
+ device.process->for_each_thread ([id, func] (thread_info *tp)
+ {
+ ze_device_thread_t tid = ze_thread_id (tp);
+ if (ze_device_thread_in (tid, id))
+ func (tp);
+ });
+}
+
+/* Add a process for DEVICE. */
+
+static process_info *
+ze_add_process (ze_device_info *device, ze_process_state state)
+{
+ gdb_assert (device != nullptr);
+
+ process_info *process = add_process (device->ordinal, 1);
+ process->priv = new process_info_private (device, state);
+ process->tdesc = device->tdesc.get ();
+ device->process = process;
+
+ /* Enumerate threads on the device we attached to.
+
+ We debug the entire device so we can enumerate all threads at once. They
+ will be idle some of the time and we won't be able to interact with them.
+ When work gets submitted to the device, the thread dispatcher will
+ distribute the work onto device threads.
+
+ The alternative of only representing threads that are currently executing
+ work would be too intrusive as we'd need to stop each thread on every
+ dispatch. */
+ long tid = 0;
+ uint32_t slice, sslice, eu, thread;
+ const ze_device_properties_t &properties = device->properties;
+ for (slice = 0; slice < properties.numSlices; ++slice)
+ for (sslice = 0; sslice < properties.numSubslicesPerSlice; ++sslice)
+ for (eu = 0; eu < properties.numEUsPerSubslice; ++eu)
+ for (thread = 0; thread < properties.numThreadsPerEU; ++thread)
+ {
+ /* We use the device ordinal as process id. */
+ ptid_t ptid = ptid_t ((int) device->ordinal, ++tid, 0l);
+
+ /* We can only support that many threads. */
+ if (tid < 0)
+ error (_("Too many threads on device %lu: %s."),
+ device->ordinal, properties.name);
+
+ /* Storing the 128b device thread id in the private data. We might
+ want to extend ptid_t and put it there so GDB can show it to the
+ user. */
+ ze_thread_info *zetp = new ze_thread_info {};
+ zetp->id.slice = slice;
+ zetp->id.subslice = sslice;
+ zetp->id.eu = eu;
+ zetp->id.thread = thread;
+
+ /* Assume threads are running until we hear otherwise. */
+ zetp->exec_state = ZE_THREAD_STATE_RUNNING;
+
+ process->add_thread (ptid, zetp);
+ }
+
+ device->nthreads = tid;
+ device->nresumed = tid;
+
+ dprintf ("process %d (%s) with %ld threads created for device %lu: %s.",
+ (int) device->ordinal, ze_process_state_str (state), tid,
+ device->ordinal, properties.name);
+
+ return process;
+}
+
+/* Remove a Level-Zero PROCESS. */
+
+static void
+ze_remove_process (process_info *process)
+{
+ gdb_assert (process != nullptr);
+
+ process->for_each_thread ([=] (thread_info *thread)
+ {
+ delete (ze_thread_info *) thread->target_data ();
+ process->remove_thread (thread);
+ });
+
+ process_info_private *zeinfo = process->priv;
+ gdb_assert (zeinfo != nullptr);
+
+ /* We may or may not have a device.
+
+ When we got detached, we will remove the device first, and remove the
+ process when we select an event from one of its threads.
+
+ When we get a process exit event, the device will remain after the process
+ has been removed. */
+ ze_device_info *device = zeinfo->device;
+ if (device != nullptr)
+ {
+ gdb_assert (device->process == process);
+ device->process = nullptr;
+ }
+
+ process->priv = nullptr;
+ delete zeinfo;
+
+ remove_process (process);
+}
+
+/* Show PROCESS. */
+
+static void
+ze_show_process (process_info *process)
+{
+ gdb_assert (process != nullptr);
+ process_info_private *priv = process->priv;
+
+ gdb_assert (priv != nullptr);
+ switch (priv->state)
+ {
+ case ZE_PROCESS_VISIBLE:
+ return;
+
+ case ZE_PROCESS_HIDDEN:
+ /* FIXME: report state change
+
+ Set priv->status and report the new visibility to GDB. */
+ priv->state = ZE_PROCESS_VISIBLE;
+ return;
+ }
+
+ internal_error (_("unknown process state"));
+}
+
+/* Hide PROCESS. */
+
+static void
+ze_hide_process (process_info *process)
+{
+ gdb_assert (process != nullptr);
+ process_info_private *priv = process->priv;
+
+ gdb_assert (priv != nullptr);
+ switch (priv->state)
+ {
+ case ZE_PROCESS_HIDDEN:
+ return;
+
+ case ZE_PROCESS_VISIBLE:
+ /* FIXME: report state change
+
+ Set priv->status and report the new visibility to GDB. */
+ priv->state = ZE_PROCESS_HIDDEN;
+ return;
+ }
+
+ internal_error (_("unknown process state"));
+}
+
+/* Attach to DEVICE and create a hidden process for it.
+
+ Modifies DEVICE as a side-effect.
+ Returns the created process or nullptr if DEVICE does not support debug. */
+
+static process_info *
+ze_attach (ze_device_info *device)
+{
+ gdb_assert (device != nullptr);
+
+ if (device->session != nullptr)
+ error (_("Already attached to %s."), device->properties.name);
+
+ device->debug_attach_state = zetDebugAttach (device->handle, &device->config,
+ &device->session);
+ switch (device->debug_attach_state)
+ {
+ case ZE_RESULT_SUCCESS:
+ if (device->session == nullptr)
+ error (_("Bad handle returned by zetDebugAttach on %s."),
+ device->properties.name);
+
+ return ze_add_process (device, ZE_PROCESS_HIDDEN);
+
+ case ZE_RESULT_NOT_READY:
+ /* We're too early. The Level-Zero user-mode driver has not been
+ initialized, yet. */
+ error (_("Attempting to attach too early to %s."),
+ device->properties.name);
+
+ case ZE_RESULT_ERROR_UNSUPPORTED_FEATURE:
+ /* Not all sub-devices support attaching to them. */
+ dprintf ("Attach not supported on %s", device->properties.name);
+ return nullptr;
+
+ case ZE_RESULT_ERROR_NOT_AVAILABLE:
+ /* Someone else is already attached. This could be us if we already
+ attached to some other sub-device in this device tree. */
+ error (_("Someone is already attached to %s."),
+ device->properties.name);
+
+ default:
+ error (_("Failed to attach to %s (%x)."), device->properties.name,
+ device->debug_attach_state);
+ }
+}
+
+/* Detach from DEVICE. */
+
+static void
+ze_detach (ze_device_info *device)
+{
+ gdb_assert (device != nullptr);
+
+ zet_debug_session_handle_t session = device->session;
+ if (session == nullptr)
+ error (_("Already detached from %s."), device->properties.name);
+
+ dprintf ("device %lu=%s", device->ordinal, device->properties.name);
+
+ ze_result_t status = zetDebugDetach (session);
+ switch (status)
+ {
+ case ZE_RESULT_ERROR_DEVICE_LOST:
+ case ZE_RESULT_SUCCESS:
+ device->session = nullptr;
+ break;
+
+ default:
+ error (_("Failed to detach from %s (%x)."), device->properties.name,
+ status);
+ }
+}
+
+/* Return a human-readable detach reason string. */
+
+static std::string
+ze_detach_reason_str (zet_debug_detach_reason_t reason)
+{
+ switch (reason)
+ {
+ case ZET_DEBUG_DETACH_REASON_INVALID:
+ return std::string (_("invalid"));
+
+ case ZET_DEBUG_DETACH_REASON_HOST_EXIT:
+ return std::string (_("the host process exited"));
+ }
+
+ return std::string (_("unknown"));
+}
+
+/* Return a human-readable module debug information format string. */
+
+static std::string
+ze_debug_info_format_str (zet_module_debug_info_format_t format)
+{
+ switch (format)
+ {
+ case ZET_MODULE_DEBUG_INFO_FORMAT_ELF_DWARF:
+ return std::string (_("DWARF"));
+ }
+
+ return std::string (_("unknown"));
+}
+
+/* Return a human-readable event string. */
+
+static std::string
+ze_event_str (const zet_debug_event_t &event)
+{
+ std::stringstream sstream;
+
+ switch (event.type)
+ {
+ case ZET_DEBUG_EVENT_TYPE_INVALID:
+ sstream << "invalid";
+ return sstream.str ();
+
+ case ZET_DEBUG_EVENT_TYPE_DETACHED:
+ sstream << "detached, reason="
+ << ze_detach_reason_str (event.info.detached.reason);
+ return sstream.str ();
+
+ case ZET_DEBUG_EVENT_TYPE_PROCESS_ENTRY:
+ sstream << "process entry";
+ return sstream.str ();
+
+ case ZET_DEBUG_EVENT_TYPE_PROCESS_EXIT:
+ sstream << "process exit";
+ return sstream.str ();
+
+ case ZET_DEBUG_EVENT_TYPE_MODULE_LOAD:
+ sstream << "module load, format="
+ << ze_debug_info_format_str (event.info.module.format)
+ << ", module=[" << std::hex << event.info.module.moduleBegin
+ << "; " << std::hex << event.info.module.moduleEnd
+ << "), addr=" << std::hex << event.info.module.load
+ << ", need-ack: "
+ << ((event.flags & ZET_DEBUG_EVENT_FLAG_NEED_ACK) != 0);
+ return sstream.str ();
+
+ case ZET_DEBUG_EVENT_TYPE_MODULE_UNLOAD:
+ sstream << "module unload, format="
+ << ze_debug_info_format_str (event.info.module.format)
+ << ", module=[" << std::hex << event.info.module.moduleBegin
+ << "; " << std::hex << event.info.module.moduleEnd
+ << "), addr=" << std::hex << event.info.module.load;
+ return sstream.str ();
+
+ case ZET_DEBUG_EVENT_TYPE_THREAD_STOPPED:
+ sstream << "thread stopped, thread="
+ << ze_thread_id_str (event.info.thread.thread);
+ return sstream.str ();
+
+ case ZET_DEBUG_EVENT_TYPE_THREAD_UNAVAILABLE:
+ sstream << "thread unavailable, thread="
+ << ze_thread_id_str (event.info.thread.thread);
+ return sstream.str ();
+ }
+
+ sstream << "unknown, code=" << event.type;
+ return sstream.str ();
+}
+
+/* Acknowledge an event, if necessary. */
+
+static void
+ze_ack_event (const ze_device_info &device, const zet_debug_event_t &event)
+{
+ /* There is nothing to do for events that do not need acknowledging. */
+ if ((event.flags & ZET_DEBUG_EVENT_FLAG_NEED_ACK) == 0)
+ return;
+
+ ze_result_t status = zetDebugAcknowledgeEvent (device.session, &event);
+ switch (status)
+ {
+ case ZE_RESULT_SUCCESS:
+ break;
+
+ default:
+ error (_("error acknowledging event: %s: %x."),
+ ze_event_str (event).c_str (), status);
+ }
+}
+
+/* Clear TP's resume state. */
+
+static void
+ze_clear_resume_state (thread_info *tp)
+{
+ ze_thread_info *zetp = ze_thread (tp);
+ gdb_assert (zetp != nullptr);
+
+ zetp->resume_state = ZE_THREAD_RESUME_NONE;
+}
+
+/* Set TP's resume state from RKIND. */
+
+static void
+ze_set_resume_state (thread_info *tp, resume_kind rkind)
+{
+ ze_thread_info *zetp = ze_thread (tp);
+ gdb_assert (zetp != nullptr);
+
+ switch (rkind)
+ {
+ case resume_continue:
+ zetp->resume_state = ZE_THREAD_RESUME_RUN;
+ return;
+
+ case resume_step:
+ zetp->resume_state = ZE_THREAD_RESUME_STEP;
+ return;
+
+ case resume_stop:
+ zetp->resume_state = ZE_THREAD_RESUME_STOP;
+ return;
+ }
+
+ internal_error (_("bad resume kind: %d."), rkind);
+}
+
+/* Return TP's resume state. */
+
+static enum ze_thread_resume_state_t
+ze_resume_state (const thread_info *tp)
+{
+ const ze_thread_info *zetp = ze_thread (tp);
+ if (zetp == nullptr)
+ return ZE_THREAD_RESUME_NONE;
+
+ return zetp->resume_state;
+}
+
+/* Return TP's execution state. */
+
+static enum ze_thread_exec_state_t
+ze_exec_state (const thread_info *tp)
+{
+ const ze_thread_info *zetp = ze_thread (tp);
+ if (zetp == nullptr)
+ return ZE_THREAD_STATE_UNKNOWN;
+
+ return zetp->exec_state;
+}
+
+/* Return whether TP has a stop execution state. */
+
+static bool
+ze_thread_stopped (const thread_info *tp)
+{
+ ze_thread_exec_state_t state = ze_exec_state (tp);
+
+ return ((state == ZE_THREAD_STATE_STOPPED)
+ || (state == ZE_THREAD_STATE_HELD)
+ || (state == ZE_THREAD_STATE_PAUSED));
+}
+
+/* Return whether TP has a pending event. */
+
+static bool
+ze_has_waitstatus (const thread_info *tp)
+{
+ const ze_thread_info *zetp = ze_thread (tp);
+ if (zetp == nullptr)
+ return false;
+
+ return (zetp->waitstatus.kind () != TARGET_WAITKIND_IGNORE);
+}
+
+/* Return whether TP has a pending priority event. */
+
+static bool
+ze_has_priority_waitstatus (const thread_info *tp)
+{
+ const ze_thread_info *zetp = ze_thread (tp);
+ if (zetp == nullptr)
+ return false;
+
+ switch (zetp->waitstatus.kind ())
+ {
+ case TARGET_WAITKIND_IGNORE:
+ case TARGET_WAITKIND_UNAVAILABLE:
+ return false;
+
+ case TARGET_WAITKIND_STOPPED:
+ /* If this thread was stopped via an interrupt, it is an interesting
+ case if GDB wanted it stopped with a stop resume request. */
+ if ((zetp->stop_reason == TARGET_STOPPED_BY_NO_REASON)
+ && (zetp->waitstatus.sig () == GDB_SIGNAL_TRAP))
+ return (zetp->resume_state == ZE_THREAD_RESUME_STOP);
+
+ /* If this thread stopped spuriously, it is not interesting. */
+ if ((zetp->stop_reason == TARGET_STOPPED_BY_NO_REASON)
+ && (zetp->waitstatus.sig () == GDB_SIGNAL_0))
+ return false;
+
+ return true;
+
+ default:
+ return true;
+ }
+}
+
+/* Return TP's waitstatus and clear it in TP. */
+
+static target_waitstatus
+ze_move_waitstatus (thread_info *tp)
+{
+ ze_thread_info *zetp = ze_thread (tp);
+ if (zetp == nullptr)
+ return {};
+
+ target_waitstatus status = zetp->waitstatus;
+ zetp->waitstatus.set_ignore ();
+
+ return status;
+}
+
+/* Indicate that we have been detached from the device corresponding to
+ PROCESS. */
+
+static void
+ze_device_detached (process_info *process, zet_debug_detach_reason_t reason)
+{
+ gdb_assert (process != nullptr);
+
+ /* We model getting detached from a device as the corresponding device process
+ exiting with the detach reason as exit status.
+
+ In the first step, we mark all threads of that process exited. We already
+ use the process exit wait status as all threads will exit together.
+
+ In the second step, when one of those threads gets selected for reporting
+ its event, we will remove the process as part of the reporting flow. */
+
+ process->for_each_thread ([reason] (thread_info *tp)
+ {
+ ze_thread_info *zetp = ze_thread (tp);
+ gdb_assert (zetp != nullptr);
+
+ zetp->waitstatus.set_exited ((int) reason);
+ });
+}
+
+/* Find the regset containing REGNO on DEVICE or throw if not found. */
+
+static ze_regset_info
+ze_find_regset (const ze_device_info &device, long regno)
+{
+ for (const ze_regset_info ®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_fetched = false;
+ regcache->reset (REG_UNKNOWN);
+}
+
+/* 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::string &args)
+{
+ /* Level-zero does not support creating inferiors. */
+ return -1;
+}
+
+int
+ze_target::attach (int pid)
+{
+ if (!devices.empty ())
+ error (_("Already attached."));
+
+ uint32_t hostpid = (uint32_t) pid;
+ if ((int) hostpid != pid)
+ error (_("Host process id is not supported."));
+
+ int ndevices = attach_to_devices (hostpid);
+ if (ndevices == 0)
+ error (_("No supported devices found."));
+
+ /* Let's check if we were able to attach to at least one device. */
+ int nattached = 0;
+ std::stringstream sstream;
+ sstream << "Failed to attach to any device.";
+ for (ze_device_info *device : devices)
+ {
+ gdb_assert (device != nullptr);
+ switch (device->debug_attach_state)
+ {
+ case ZE_RESULT_SUCCESS:
+ if (device->session == nullptr)
+ {
+ sstream << "\nDevice " << device->ordinal << " ["
+ << device->properties.name << "] : "
+ << "failed to initialize debug session";
+ continue;
+ }
+
+ /* GDB (and higher layers of gdbserver) expects threads stopped on
+ attach in all-stop mode. In non-stop mode, GDB explicitly
+ sends a stop request. */
+ if (!non_stop)
+ {
+ device->process->for_each_thread ([this] (thread_info *tp)
+ {
+ ze_set_resume_state (tp, resume_stop);
+ bool should_stop = ze_prepare_for_stopping (tp);
+ gdb_assert (should_stop);
+ });
+
+ ze_device_thread_t all = ze_thread_id_all ();
+ ze_interrupt (*device, all);
+ }
+
+ nattached += 1;
+ break;
+ case ZE_RESULT_NOT_READY:
+ sstream << "\nDevice " << device->ordinal << " ["
+ << device->properties.name << "] : "
+ << "attempting to attach too early";
+ break;
+ case ZE_RESULT_ERROR_UNSUPPORTED_FEATURE:
+ sstream << "\nDevice " << device->ordinal << " ["
+ << device->properties.name << "] : "
+ << "attaching is not supported";
+ break;
+ case ZE_RESULT_ERROR_INSUFFICIENT_PERMISSIONS:
+ sstream << "\nDevice " << device->ordinal << " ["
+ << device->properties.name << "] : "
+ << "attaching is not permitted";
+ break;
+ case ZE_RESULT_ERROR_NOT_AVAILABLE:
+ sstream << "\nDevice " << device->ordinal << " ["
+ << device->properties.name << "] : "
+ << "a debugger is already attached";
+ break;
+ default:
+ sstream << "\nDevice " << device->ordinal << " ["
+ << device->properties.name << "] : "
+ << "failed to attach with error code '"
+ << std::hex << device->debug_attach_state
+ << std::resetiosflags (std::ios::basefield)
+ << "'";
+ break;
+ }
+ }
+
+ if (nattached == 0)
+ error (_("%s"), sstream.str ().c_str ());
+
+ /* In all-stop mode above, we interrupted the devices. Now we make sure
+ they come to a stop state. So, we fetch events until no device has any
+ resumed threads left. There might be low priority events (e.g.
+ 'module load', 'process entry') we should fetch before fetching higher
+ priority events in the subsequent call of 'wait ()'. If not done here,
+ we fetch the lower priority events in 'wait ()', report an UNAVAILABLE
+ status to GDB and then fetch the higher priority events in 'pause_all'.
+ In a live attach scenario, we don't receive a 'continue' resume request
+ and would miss the thread stopped event. */
+ if (!non_stop)
+ fetch_events_all_devices_no_resumed ();
+
+ /* Return the ID of the last device we attached to. */
+ int device_pid = ze_device_pid (*(devices.back ()));
+ return device_pid;
+}
+
+int
+ze_target::detach (process_info *proc)
+{
+ gdb_assert (proc != nullptr);
+
+ process_info_private *priv = proc->priv;
+ gdb_assert (priv != nullptr);
+
+ ze_device_info *device = priv->device;
+ if (device != nullptr)
+ {
+ /* Resume all the threads on the device. GDB must have already
+ removed all the breakpoints. */
+ try
+ {
+ /* Clear all the pending events first. */
+ proc->for_each_thread ([] (thread_info *tp)
+ {
+ (void) ze_move_waitstatus (tp);
+ });
+
+ resume (*device);
+ }
+ catch (const gdb_exception_error &except)
+ {
+ /* Swallow the error. We are detaching anyway. */
+ dprintf ("%s", except.what ());
+ }
+
+ ze_detach (device);
+ }
+
+ mourn (proc);
+ return 0;
+}
+
+int
+ze_target::kill (process_info *proc)
+{
+ /* Level-zero does not support killing inferiors. */
+ return -1;
+}
+
+void
+ze_target::mourn (process_info *proc)
+{
+ ze_remove_process (proc);
+}
+
+void
+ze_target::join (int pid)
+{
+ /* Nothing to do for Level-Zero targets. */
+}
+
+void
+ze_target::resume (ze_device_info &device)
+{
+ gdb_assert (device.process != nullptr);
+
+ bool has_thread_to_resume = false;
+ device.process->for_each_thread ([&] (thread_info *tp)
+ {
+ ze_set_resume_state (tp, resume_continue);
+ if (ze_prepare_for_resuming (tp))
+ {
+ prepare_thread_resume (tp);
+ regcache_invalidate_thread (tp);
+ has_thread_to_resume = true;
+ }
+ });
+
+ /* There is nothing to resume if nothing is stopped. */
+ if (!has_thread_to_resume)
+ return;
+
+ ze_device_thread_t all = ze_thread_id_all ();
+ ze_resume (device, all);
+}
+
+void
+ze_target::resume_single_thread (thread_info *thread)
+{
+ ze_device_info *device = ze_thread_device (thread);
+ gdb_assert (device != nullptr);
+ ze_thread_info *zetp = ze_thread (thread);
+ gdb_assert (zetp != nullptr);
+
+ bool should_resume = ze_prepare_for_resuming (thread);
+ gdb_assert (should_resume);
+ prepare_thread_resume (thread);
+ regcache_invalidate_thread (thread);
+ ze_resume (*device, zetp->id);
+}
+
+size_t
+ze_target::mark_eventing_threads (ptid_t resume_ptid, resume_kind rkind)
+{
+ /* Note that even if we stopped all, unavailable threads may still
+ report new events as we were not able to stop them.
+
+ We ignore those threads and the unavailable event they report. */
+
+ size_t num_eventing = 0;
+ for_each_thread ([=, &num_eventing] (thread_info *tp)
+ {
+ if (!tp->id.matches (resume_ptid))
+ return;
+
+ if (!ze_has_priority_waitstatus (tp))
+ {
+ (void) ze_move_waitstatus (tp);
+ return;
+ }
+
+ ze_thread_info *zetp = ze_thread (tp);
+ gdb_assert (zetp != nullptr);
+
+ /* If the thread's stop event was being held, it is now the time
+ to convert the state to 'stopped' to unleash the event. */
+ if (zetp->exec_state == ZE_THREAD_STATE_HELD)
+ zetp->exec_state = ZE_THREAD_STATE_STOPPED;
+
+ /* TP may have stopped at a breakpoint that is already deleted
+ by GDB. Consider TP as an eventing thread only if the BP is
+ still there. Because we are inside the 'resume' request, if
+ the BP is valid, GDB must have already re-inserted it.
+
+ FIXME: Keep track of the stop_pc and compare it with the
+ current (i.e. to-be-resumed) pc. */
+ if ((zetp->exec_state == ZE_THREAD_STATE_STOPPED)
+ && (zetp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT)
+ && !is_at_breakpoint (tp))
+ {
+ /* The BP is gone. Clear the waitstatus, too. */
+ target_waitstatus waitstatus = ze_move_waitstatus (tp);
+ if (waitstatus.kind () != TARGET_WAITKIND_STOPPED)
+ warning (_("thread %s has waitstatus %s, expected 'STOPPED'."),
+ tp->id.to_string ().c_str (),
+ waitstatus.to_string ().c_str ());
+ return;
+ }
+
+ /* TP may have stopped during range-stepping, but we reported
+ another thread to GDB. This means the range-stepping state
+ of TP is canceled.
+
+ The condition here is similar to, but not the same as
+ is_range_stepping. We do not check here if the thread's stop
+ pc is within the stepping range. We rather only check if there
+ was a range to step, because the thread may have stopped just
+ when it came out of the range. We should cancel the event in
+ that case, too. */
+ if (ze_thread_stopped (tp)
+ && (zetp->stop_reason == TARGET_STOPPED_BY_SINGLE_STEP)
+ && (zetp->step_range_end > zetp->step_range_start))
+ {
+ target_waitstatus waitstatus = ze_move_waitstatus (tp);
+ dprintf ("Thread %s (%s) was range-stepping, "
+ "canceling the pending event",
+ tp->id.to_string ().c_str (),
+ ze_thread_id_str (zetp->id).c_str ());
+ return;
+ }
+
+ /* Recover the resume state so that the thread can be picked up
+ by 'wait'. */
+ ze_set_resume_state (tp, rkind);
+ num_eventing++;
+ });
+
+ dprintf ("there are %zu eventing threads for ptid %s", num_eventing,
+ resume_ptid.to_string ().c_str ());
+
+ return num_eventing;
+}
+
+/* Display a resume request for logging purposes. */
+
+static void
+print_resume_info (const thread_resume &rinfo)
+{
+ ptid_t rptid = rinfo.thread;
+
+ switch (rinfo.kind)
+ {
+ case resume_continue:
+ dprintf ("received 'continue' resume request for (%s)",
+ rptid.to_string ().c_str ());
+ return;
+
+ case resume_step:
+ dprintf ("received 'step' resume request for (%s)"
+ " in range [0x%" PRIx64 ", 0x%" PRIx64 ")",
+ rptid.to_string ().c_str (),
+ rinfo.step_range_start, rinfo.step_range_end);
+ return;
+
+ case resume_stop:
+ dprintf ("received 'stop' resume request for (%s)",
+ rptid.to_string ().c_str ());
+ return;
+ }
+
+ internal_error (_("bad resume kind: %d."), rinfo.kind);
+}
+
+/* Normalize the resume requests for easier processing later on. */
+
+static void
+normalize_resume_infos (thread_resume *resume_info, size_t n)
+{
+ for (size_t i = 0; i < n; ++i)
+ {
+ thread_resume &rinfo = resume_info[i];
+ ptid_t rptid = rinfo.thread;
+
+ /* Log the original requests. */
+ print_resume_info (rinfo);
+
+ /* We convert ptids of the form (p, -1, 0) to (p, 0, 0) to make
+ 'ptid.matches' work. This transformation is safe because we
+ enumerate the threads starting at 1. */
+ if ((rptid.lwp () == -1) && (rptid.pid () > 0))
+ rinfo.thread = ptid_t (rptid.pid (), 0, 0);
+
+ if (rinfo.sig != 0)
+ {
+ /* Clear out the signal. Our target does not accept
+ signals. */
+ warning (_("Ignoring signal on resume request for %s"),
+ rinfo.thread.to_string ().c_str ());
+ rinfo.sig = 0;
+ }
+ }
+}
+
+/* Resuming threads of a device all at once with a single API call
+ is preferable to resuming threads individually. Therefore, we
+ want to combine individual resume requests with wildcard resumes,
+ if possible.
+
+ For instance, if we receive "vCont;s:1;s:2;c", we would like to
+ make a single ze_resume call with the 'all.all.all.all' thread id
+ after preparing threads 1 and 2 for stepping and the others for
+ continuing.
+
+ We preprocess the resume requests to find for which devices we
+ shall combine the requests. We attempt a merge in all-stop mode
+ when the requests contain continue/step requests only. */
+
+static std::set<ze_device_info *>
+find_wildcard_devices (thread_resume *resume_info, size_t n,
+ const std::list<ze_device_info *> &devices)
+{
+ std::set<ze_device_info *> wildcard_devices;
+
+ if (non_stop)
+ return wildcard_devices;
+
+ for (size_t i = 0; i < n; ++i)
+ {
+ if (resume_info[i].kind == resume_stop)
+ {
+ wildcard_devices.clear ();
+ break;
+ }
+
+ ptid_t rptid = resume_info[i].thread;
+ if (rptid == minus_one_ptid)
+ {
+ for (ze_device_info *device : devices)
+ wildcard_devices.insert (device);
+ break;
+ }
+
+ if (rptid.is_pid ())
+ {
+ process_info *proc = find_process_pid (rptid.pid ());
+ ze_device_info *device = ze_process_device (proc);
+ if (device != nullptr)
+ wildcard_devices.insert (device);
+ }
+ }
+
+ return wildcard_devices;
+}
+
+void
+ze_target::resume (thread_resume *resume_info, size_t n)
+{
+ if (frozen)
+ return;
+
+ /* In all-stop mode, a new resume request overwrites any previous
+ request. We're going to set the request for affected threads below.
+ Clear it for all threads, here.
+
+ In the resume-all case, this will iterate over all threads twice to
+ first clear and then set the resume request. Not ideal, but if we
+ first iterated over all threads to set the resume state, we'd also
+ have to iterate over all threads again in order to actually resume
+ them.
+
+ And if we inverted the loops (i.e. iterate over threads, then over
+ resume requests), we'd miss out on the opportunity to resume all
+ threads at once. */
+ if (!non_stop)
+ for_each_thread ([] (thread_info *tp)
+ {
+ ze_clear_resume_state (tp);
+ });
+
+ normalize_resume_infos (resume_info, n);
+
+ /* Check if there is a thread with a pending event for any of the
+ resume requests. In all-stop mode, we would omit actually
+ resuming the target if there is such a thread. In non-stop mode,
+ we omit resuming the thread itself. */
+ size_t num_eventing = 0;
+ for (size_t i = 0; i < n; ++i)
+ {
+ const thread_resume &rinfo = resume_info[i];
+ resume_kind rkind = rinfo.kind;
+ ptid_t rptid = rinfo.thread;
+
+ if (rkind == resume_stop)
+ continue;
+
+ num_eventing += mark_eventing_threads (rptid, rkind);
+ }
+
+ if ((num_eventing > 0) && !non_stop)
+ return;
+
+ std::set<ze_device_info *> wildcard_devices
+ = find_wildcard_devices (resume_info, n, devices);
+
+ std::set<ze_device_info *> devices_to_resume;
+
+ /* Lambda for applying a resume info on a single thread. */
+ auto apply_resume_info = ([&] (const thread_resume &rinfo,
+ thread_info *tp)
+ {
+ if (ze_has_priority_waitstatus (tp))
+ return;
+
+ ze_set_resume_state (tp, rinfo.kind);
+ ze_device_info *device = ze_thread_device (tp);
+ ze_device_thread_t tid = ze_thread_id (tp);
+
+ switch (rinfo.kind)
+ {
+ case resume_stop:
+ if (ze_prepare_for_stopping (tp))
+ ze_interrupt (*device, tid);
+ break;
+
+ case resume_step:
+ {
+ ze_thread_info *zetp = ze_thread (tp);
+ gdb_assert (zetp != nullptr);
+
+ regcache *regcache
+ = get_thread_regcache (tp, /* fetch = */ true);
+ CORE_ADDR pc = read_pc (regcache);
+
+ /* For single-stepping, start == end. Typically, both are 0.
+ For range-stepping, the PC must be within the range. */
+ CORE_ADDR start = rinfo.step_range_start;
+ CORE_ADDR end = rinfo.step_range_end;
+ gdb_assert ((start == end) || ((pc >= start) && (pc < end)));
+
+ zetp->step_range_start = start;
+ zetp->step_range_end = end;
+ }
+
+ [[fallthrough]];
+ case resume_continue:
+ if (ze_prepare_for_resuming (tp))
+ {
+ prepare_thread_resume (tp);
+ regcache_invalidate_thread (tp);
+
+ /* If the device can be resumed as a whole,
+ omit resuming the thread individually. */
+ if (wildcard_devices.count (device) == 0)
+ ze_resume (*device, tid);
+ else
+ devices_to_resume.insert (device);
+ }
+ break;
+ }
+ });
+
+ /* We may receive multiple requests that apply to a thread. E.g.
+ "vCont;r0xff10,0xffa0:p1.9;c" could be sent to make thread 1.9 do
+ range-stepping from 0xff10 to 0xffa0, while continuing others.
+ According to the Remote Protocol Section E.2 (Packets),
+ "For each inferior thread, the leftmost action with a matching
+ thread-id is applied." For this reason, we keep track of which
+ threads have been resumed individually so that we can skip them
+ when processing wildcard requests.
+
+ Alternatively, we could have the outer loop iterate over threads
+ and the inner loop iterate over resume infos to find the first
+ matching resume info for each thread. There may, however, be a
+ large number of threads and a handful of resume infos that apply
+ to a few threads only. For performance reasons, we prefer to
+ iterate over resume infos in the outer loop. */
+ std::set<thread_info *> individually_resumed_threads;
+ for (size_t i = 0; i < n; ++i)
+ {
+ const thread_resume &rinfo = resume_info[i];
+ gdb_assert (rinfo.sig == 0);
+ ptid_t rptid = rinfo.thread;
+ int rpid = rptid.pid ();
+ if ((rptid == minus_one_ptid)
+ || rptid.is_pid ()
+ || (rptid.lwp () == -1))
+ {
+ for (ze_device_info *device : devices)
+ {
+ gdb_assert (device != nullptr);
+
+ int pid = ze_device_pid (*device);
+ if ((rpid != -1) && (rpid != pid))
+ continue;
+
+ device->process->for_each_thread ([&] (thread_info *tp)
+ {
+ /* We trust that GDB will not send us wildcard resume
+ requests with overlapping pids. Hence, we track
+ only individually-resumed threads. */
+ if (individually_resumed_threads.count (tp) == 0)
+ apply_resume_info (rinfo, tp);
+ });
+ }
+ }
+ else
+ {
+ thread_info *tp = find_thread_ptid (rptid);
+ apply_resume_info (rinfo, tp);
+ individually_resumed_threads.insert (tp);
+ }
+ }
+
+ /* Finally, resume the whole devices. */
+ ze_device_thread_t all = ze_thread_id_all ();
+ for (ze_device_info *device : devices_to_resume)
+ ze_resume (*device, all);
+}
+
+/* Look for a thread preferably with a priority stop
+ event. If we cannot find such an event, we look for an
+ interrupt-related stop event, e.g. a stop because of an
+ external Ctrl-C or an internal pause_all request. We pick
+ a THREAD_UNAVAILABLE event for reporting as the last resort.
+
+ We first make an iteration over the threads to figure out
+ what kind of an event we can report. Once found, we select
+ a thread randomly.
+
+ In all-stop mode, we will ignore unavailable threads when
+ resuming the target. So, unless we explicitly try to interact
+ with them, unavailable threads should be transparent to an
+ all-stop target.
+
+ In non-stop mode, we give more time for unavailable threads to
+ become available and report an event. */
+
+static thread_info *
+ze_find_eventing_thread (ptid_t ptid)
+{
+ using thread_predicate = bool (*) (const thread_info *);
+ thread_predicate is_stopped = [] (const thread_info *tp)
+ {
+ return (ze_thread (tp)->waitstatus.kind () == TARGET_WAITKIND_STOPPED);
+ };
+
+ thread_predicate predicate = nullptr;
+ find_thread (ptid, [&] (thread_info *tp)
+ {
+ /* Only consider threads that were resumed. */
+ ze_thread_resume_state_t state = ze_resume_state (tp);
+ if (state == ZE_THREAD_RESUME_NONE)
+ return false;
+
+ /* If this thread's event is being held, we do not pick it for
+ reporting. */
+ ze_thread_exec_state_t exec_state = ze_exec_state (tp);
+ if (exec_state == ZE_THREAD_STATE_HELD)
+ return false;
+
+ if (ze_has_priority_waitstatus (tp))
+ {
+ predicate = ze_has_priority_waitstatus;
+ return true;
+ }
+
+ if (is_stopped (tp))
+ predicate = is_stopped;
+ else if ((predicate == nullptr) && ze_has_waitstatus (tp))
+ predicate = ze_has_waitstatus;
+
+ return false;
+ });
+
+ thread_info *thread = nullptr;
+ if (predicate != nullptr)
+ thread = find_thread_in_random (ptid, [predicate] (thread_info *tp)
+ {
+ /* Only consider threads that were resumed. */
+ ze_thread_resume_state_t state = ze_resume_state (tp);
+ if (state == ZE_THREAD_RESUME_NONE)
+ return false;
+
+ /* Threads with held events are not picked. */
+ ze_thread_exec_state_t exec_state = ze_exec_state (tp);
+ if (exec_state == ZE_THREAD_STATE_HELD)
+ return false;
+
+ return predicate (tp);
+ });
+ return thread;
+}
+
+ptid_t
+ze_target::wait (ptid_t ptid, target_waitstatus *status,
+ target_wait_flags options)
+{
+ /* We need to wait for further events. */
+ ze_async_mark ();
+
+ do
+ {
+ /* We start by fetching all events.
+
+ This will mark threads stopped and also process solist updates. We may
+ get solist updates even if all device threads are running.
+
+ For all-stop, we anyway want to stop all threads and drain events
+ before reporting the stop to GDB.
+
+ For non-stop, this will allow us to group stop events for multiple
+ threads. */
+ uint64_t nevents;
+ do
+ {
+ nevents = 0;
+
+ for (ze_device_info *device : devices)
+ {
+ gdb_assert (device != nullptr);
+ /* Fetch from any device, regardless of PTID, so that we
+ drain the event queues as much as possible. We use
+ PTID down below to filter the events anyway. */
+ nevents += fetch_events (*device);
+ }
+ }
+ while (nevents > 0);
+
+ /* Next, find a matching entity, whose event we'll report.
+
+ We prioritize process events since they are typically a lot rarer and
+ further have higher impact and should be handled before any thread
+ events of that process.
+
+ Process events are no stop events. They leave threads running,
+ even in all-stop mode. */
+ process_info *process
+ = find_process ([ptid, this] (process_info *proc)
+ {
+ if (!ptid_t (proc->pid).matches (ptid))
+ return false;
+
+ process_info_private *zeproc = proc->priv;
+ gdb_assert (zeproc != nullptr);
+
+ return (zeproc->waitstatus.kind () != TARGET_WAITKIND_IGNORE);
+ });
+
+ /* If we found a process event, it is our primary candidate.
+
+ Process events with a low priority UNAVAILABLE waitstatus do not
+ stop the target in all-stop, but some of its threads might have a
+ pending waitstatus, which requires the stop. If such THREAD is
+ found, we prioritize it, clean the process waitstatus, and fall
+ through to the thread reporting. The process event will
+ piggyback on it.
+
+ We do not take any special care about fairness as we expect process
+ events to be rather rare. */
+ thread_info *thread = nullptr;
+ if (process != nullptr)
+ {
+ process_info_private *zeproc = process->priv;
+ gdb_assert (zeproc != nullptr);
+ ptid_t process_ptid = ptid_t (process->pid);
+
+ /* If we got an unavailable process event, try to find another
+ eventing thread for this process. */
+ if (zeproc->waitstatus.kind () == TARGET_WAITKIND_UNAVAILABLE)
+ thread = ze_find_eventing_thread (process_ptid);
+
+ /* If not found, return the process and clean its waitstatus. */
+ if (thread == nullptr)
+ {
+ *status = zeproc->waitstatus;
+ zeproc->waitstatus.set_ignore ();
+
+ return process_ptid;
+ }
+
+ /* THREAD should always match the PTID: we got a process event,
+ so PTID must be either minus_one or the process's ptid. */
+ gdb_assert (thread->id.matches (ptid));
+
+ /* The process event will piggyback onto the THREAD event.
+ However, we still need to clean the process status. */
+ zeproc->waitstatus.set_ignore ();
+ }
+
+ /* If we have previously found THREAD for the PROCESS, we use it.
+ Otherwise, proceed with searching for a thread event for PTID. */
+ if (thread == nullptr)
+ thread = ze_find_eventing_thread (ptid);
+
+ if (thread != nullptr)
+ {
+ ze_thread_info *zetp = ze_thread (thread);
+ gdb_assert (zetp != nullptr);
+
+ if (is_range_stepping (thread))
+ {
+ /* We are inside the stepping range. Resume the thread
+ and go back to fetching events. */
+ dprintf ("thread %s is stepping in range "
+ "[0x%" PRIx64 ", 0x%" PRIx64 ")",
+ ze_thread_id_str (zetp->id).c_str (),
+ zetp->step_range_start, zetp->step_range_end);
+
+ zetp->waitstatus.set_ignore ();
+ gdb_assert (zetp->resume_state == ZE_THREAD_RESUME_STEP);
+
+ resume_single_thread (thread);
+ continue;
+ }
+
+ /* Resume any thread we didn't want stopped. */
+ if ((zetp->stop_reason == TARGET_STOPPED_BY_NO_REASON)
+ && (zetp->waitstatus.kind () == TARGET_WAITKIND_STOPPED)
+ && (zetp->waitstatus.sig () == GDB_SIGNAL_0))
+ {
+ dprintf ("silently resuming thread %s (%s)",
+ thread->id.to_string ().c_str (),
+ ze_thread_id_str (zetp->id).c_str ());
+
+ /* Undo any previous holding of the event. */
+ zetp->exec_state = ZE_THREAD_STATE_STOPPED;
+ zetp->waitstatus.set_ignore ();
+ ze_set_resume_state (thread, resume_continue);
+
+ resume_single_thread (thread);
+ continue;
+ }
+
+ /* Stop all other threads.
+
+ Save the waitstatus before, because pause_all clears all
+ low-priority events. */
+ *status = zetp->waitstatus;
+
+ if (!non_stop)
+ pause_all (false);
+
+ /* Now also clear the thread's event, regardless of its
+ priority. */
+ zetp->waitstatus.set_ignore ();
+ zetp->step_range_start = 0;
+ zetp->step_range_end = 0;
+
+ /* FIXME: switch_to_thread
+
+ Why isn't the caller switching based on the returned ptid? */
+ switch_to_thread (thread);
+ return thread->id;
+ }
+
+ std::this_thread::yield ();
+ }
+ while ((options & TARGET_WNOHANG) == 0);
+
+ /* We only get here if we did not find any event to report. */
+
+ status->set_ignore ();
+ return null_ptid;
+}
+
+void
+ze_target::fetch_registers (regcache *regcache, int regno)
+{
+ ze_device_thread_t tid = ze_thread_id (regcache->thread);
+ ze_device_info *device = ze_thread_device (regcache->thread);
+ gdb_assert (device != nullptr);
+
+ if (regno == -1)
+ ze_fetch_all_registers (*device, tid, regcache);
+ else
+ ze_fetch_register (*device, tid, regcache, regno);
+}
+
+void
+ze_target::store_registers (regcache *regcache, int regno)
+{
+ ze_device_thread_t tid = ze_thread_id (regcache->thread);
+ ze_device_info *device = ze_thread_device (regcache->thread);
+ gdb_assert (device != nullptr);
+
+ if (regno == -1)
+ ze_store_all_registers (*device, tid, regcache);
+ else
+ ze_store_register (*device, tid, regcache, regno);
+}
+
+/* Determine the thread id and device context for accessing ADDR_SPACE
+ from THREAD. */
+static std::pair<ze_device_thread_t, ze_device_info *>
+ze_memory_access_context (thread_info *thread, unsigned int addr_space)
+{
+ /* With a stopped thread, we can access all address spaces, and we
+ should be able to determine the device for that thread. */
+ if (ze_thread_stopped (thread))
+ return std::pair<ze_device_thread_t, ze_device_info *>
+ { ze_thread_id (thread), ze_thread_device (thread) };
+
+ /* Without a stopped thread, we may only access the default address
+ space and only in the context of thread ALL. */
+ if (addr_space != ZET_DEBUG_MEMORY_SPACE_TYPE_DEFAULT)
+ error (_("need thread to access non-default address space."));
+
+ /* Try to determine the device using THREAD but fall back to the current
+ process' device, e.g. if THREAD is nullptr. */
+ ze_device_info *device = ze_thread_device (thread);
+ if (device == nullptr)
+ {
+ process_info *process = current_process ();
+ device = ze_process_device (process);
+
+ if (device == nullptr)
+ error (_("cannot determine device for memory access."));
+ }
+
+ return std::pair<ze_device_thread_t, ze_device_info *>
+ { ze_thread_id_all (), device };
+}
+
+int
+ze_target::read_memory (thread_info *tp, CORE_ADDR memaddr,
+ unsigned char *myaddr, int len)
+{
+ unsigned int addr_space = 0; /* Only the default space for now. */
+ zet_debug_memory_space_desc_t desc;
+
+ memset (&desc, 0, sizeof (desc));
+ desc.stype = ZET_STRUCTURE_TYPE_DEBUG_MEMORY_SPACE_DESC;
+ desc.pNext = nullptr;
+ desc.type = (zet_debug_memory_space_type_t) addr_space;
+ desc.address = (uint64_t) memaddr;
+
+ std::pair<ze_device_thread_t, ze_device_info *> context
+ = ze_memory_access_context (tp, addr_space);
+ ze_device_thread_t thread = context.first;
+ ze_device_info *device = context.second;
+ gdb_assert (device != nullptr);
+
+ ze_result_t status = zetDebugReadMemory (device->session, thread, &desc,
+ len, myaddr);
+ switch (status)
+ {
+ case ZE_RESULT_SUCCESS:
+ return 0;
+
+ default:
+ dprintf ("error reading %d bytes of memory from %s with %s: %x",
+ len, core_addr_to_string_nz (memaddr),
+ ze_thread_id_str (thread).c_str (), status);
+
+ return EIO;
+ }
+}
+
+int
+ze_target::read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
+{
+ return read_memory (current_thread, memaddr, myaddr, len);
+}
+
+int
+ze_target::write_memory (thread_info *tp, CORE_ADDR memaddr,
+ const unsigned char *myaddr, int len)
+{
+ unsigned int addr_space = 0; /* Only the default space for now. */
+ zet_debug_memory_space_desc_t desc;
+
+ memset (&desc, 0, sizeof (desc));
+ desc.stype = ZET_STRUCTURE_TYPE_DEBUG_MEMORY_SPACE_DESC;
+ desc.pNext = nullptr;
+ desc.type = (zet_debug_memory_space_type_t) addr_space;
+ desc.address = (uint64_t) memaddr;
+
+ std::pair<ze_device_thread_t, ze_device_info *> context
+ = ze_memory_access_context (tp, addr_space);
+ ze_device_thread_t thread = context.first;
+ ze_device_info *device = context.second;
+ gdb_assert (device != nullptr);
+
+ dprintf ("writing %d bytes of memory to %s with %s",
+ len, core_addr_to_string_nz (memaddr),
+ ze_thread_id_str (thread).c_str ());
+
+ ze_result_t status = zetDebugWriteMemory (device->session, thread, &desc,
+ len, myaddr);
+ switch (status)
+ {
+ case ZE_RESULT_SUCCESS:
+ return 0;
+
+ default:
+ dprintf ("error writing %d bytes of memory to %s with %s: %x",
+ len, core_addr_to_string_nz (memaddr),
+ ze_thread_id_str (thread).c_str (), status);
+
+ return EIO;
+ }
+}
+
+int
+ze_target::write_memory (CORE_ADDR memaddr, const unsigned char *myaddr,
+ int len)
+{
+ return write_memory (current_thread, memaddr, myaddr, len);
+}
+
+bool
+ze_target::thread_stopped (struct thread_info *tp)
+{
+ const ze_thread_info *zetp = ze_thread (tp);
+ gdb_assert (zetp != nullptr);
+
+ return (zetp->exec_state == ZE_THREAD_STATE_STOPPED);
+}
+
+void
+ze_target::request_interrupt ()
+{
+ if (current_process () == nullptr)
+ error (_("no current process."));
+
+ process_info *process = current_process ();
+ gdb_assert (process != nullptr);
+
+ process_info_private *priv = process->priv;
+ gdb_assert (priv != nullptr);
+
+ /* The only reason why we would not have a device is if we got detached.
+
+ There is nothing to interrupt in that case. */
+ ze_device_info *device = priv->device;
+ if (device == nullptr)
+ return;
+
+ /* Interrupt is not a resume request. */
+
+ ze_device_thread_t all = ze_thread_id_all ();
+ ze_interrupt (*device, all);
+}
+
+void
+ze_target::pause_all (bool freeze)
+{
+ dprintf ("freeze: %d", freeze);
+
+ if (freeze)
+ {
+ if (frozen == UINT32_MAX)
+ internal_error (_("freeze count overflow"));
+ frozen += 1;
+ }
+
+ /* Nothing to stop if we were frozen already. */
+ if (frozen > 1)
+ return;
+
+ /* Interrupting all threads on devices that have any resumed threads.
+
+ Threads that are already stopped will be ignored by the interrupt. */
+ ze_device_thread_t all = ze_thread_id_all ();
+ for (ze_device_info *device : devices)
+ {
+ gdb_assert (device != nullptr);
+
+ /* Ignore devices we're not modelling as processes. */
+ if (device->process == nullptr)
+ continue;
+
+ if ((device->nresumed != 0) && (device->ninterrupts == 0))
+ ze_interrupt (*device, all);
+ }
+
+ /* Fetch events until no device has any resumed threads left. */
+ fetch_events_all_devices_no_resumed ();
+
+ /* Mark threads we interrupted paused so unpause_all can find then. */
+ for_each_thread ([] (thread_info *tp)
+ {
+ /* A thread without waitstatus has already been processed by a
+ previous pause_all or it has reported its event to higher layers
+ via wait.
+
+ Don't mark it paused. It either already is, if it was stopped by
+ a previous pause_all, or higher layers assume it to be stopped so
+ we don't want it so be resumed by unpause_all. */
+ if (!ze_has_waitstatus (tp))
+ return;
+
+ /* Do not mark threads that wait would pick, even if their event was
+ only just fetched. */
+ if (ze_has_priority_waitstatus (tp))
+ return;
+
+ ze_thread_info *zetp = ze_thread (tp);
+ gdb_assert (zetp != nullptr);
+
+ /* Clear the non-priority waitstatus so wait doesn't pick the thread
+ to report an (unavailable) event we just fetched. */
+ zetp->waitstatus.set_ignore ();
+
+ /* Ignore threads that aren't stopped, most likely because they are
+ unavailable.
+
+ Even though an unavailable thread may have responded to our
+ interrupt, we do not mark it paused because we need to treat
+ unavailable and stopped threads differently in unpause_all. */
+ if (ze_thread_stopped (tp))
+ zetp->exec_state = ZE_THREAD_STATE_PAUSED;
+ });
+}
+
+void
+ze_target::unpause_all (bool unfreeze)
+{
+ dprintf ("freeze: %d", unfreeze);
+
+ if (unfreeze)
+ {
+ if (frozen == 0)
+ internal_error (_("freeze count underflow"));
+ frozen -= 1;
+ }
+
+ /* Nothing to resume if we're still frozen. */
+ if (frozen > 1)
+ return;
+
+ /* Resume threads that were marked by pause_all as well as unavailable
+ threads that were not requested to stop.
+
+ Pause_all leaves the latter marked unavailable. We don't really
+ resume them as they were not actually stopped on the target, but we
+ need to update the thread state and some statistics. */
+
+ /* Check which devices are safe to be resumed and which need to be
+ checked for individual threads to be resumed.
+
+ In all-stop mode, finding a single thread would already block the
+ unpause. We do not expect this to be performance critical (or used
+ at all), however, so let's unify all-stop and non-stop as much as
+ possible. */
+ std::set<ze_device_info *> devices_to_check;
+ std::set<ze_device_info *> devices_to_resume {devices.begin (),
+ devices.end ()};
+
+ for_each_thread ([&] (thread_info *tp)
+ {
+ ze_thread_exec_state_t state = ze_exec_state (tp);
+ switch (state)
+ {
+ case ZE_THREAD_STATE_PAUSED:
+ return;
+
+ case ZE_THREAD_STATE_UNAVAILABLE:
+ {
+ /* Distinguish unavailable threads that we tried to interrupt
+ in pause_all from those that GDB tried to interrupt with a
+ stop resume request. */
+ ze_thread_resume_state_t resume_state = ze_resume_state (tp);
+ if (resume_state != ZE_THREAD_RESUME_STOP)
+ return;
+ }
+
+ [[fallthrough]];
+ case ZE_THREAD_STATE_STOPPED:
+ case ZE_THREAD_STATE_HELD:
+ {
+ ze_device_info *device = ze_thread_device (tp);
+ if (device == nullptr)
+ return;
+
+ devices_to_check.insert (device);
+ devices_to_resume.erase (device);
+ }
+ return;
+
+ case ZE_THREAD_STATE_RUNNING:
+ warning (_("thread %d.%ld running in unpause"), tp->id.pid (),
+ tp->id.lwp ());
+ return;
+
+ case ZE_THREAD_STATE_UNKNOWN:
+ warning (_("thread %d.%ld has unknown execution "
+ "state"), tp->id.pid (), tp->id.lwp ());
+ return;
+ }
+
+ internal_error (_("bad execution state: %d."), state);
+ });
+
+ /* In all-stop mode, any device that cannot be resumed aborts unpause. */
+ if (!non_stop && !devices_to_check.empty ())
+ return;
+
+ /* Resume individual threads.
+
+ In all-stop mode, this will be empty. */
+ for (ze_device_info *device : devices_to_check)
+ {
+ gdb_assert (device != nullptr);
+
+ device->process->for_each_thread ([this] (thread_info *tp)
+ {
+ ze_thread_info *zetp = ze_thread (tp);
+ gdb_assert (zetp != nullptr);
+
+ ze_thread_exec_state_t state = zetp->exec_state;
+ switch (state)
+ {
+ case ZE_THREAD_STATE_STOPPED:
+ case ZE_THREAD_STATE_HELD:
+ case ZE_THREAD_STATE_RUNNING:
+ case ZE_THREAD_STATE_UNKNOWN:
+ /* We already diagnosed unexpected states above. */
+ return;
+
+ case ZE_THREAD_STATE_UNAVAILABLE:
+ {
+ /* Don't touch threads that GDB wants stopped. */
+ ze_thread_resume_state_t resume_state = ze_resume_state (tp);
+ if (resume_state == ZE_THREAD_RESUME_STOP)
+ return;
+
+ /* We don't plan to resume but we still need to prepare TP
+ for nresumed tracking and thread state management. */
+ bool should_resume = ze_prepare_for_resuming (tp);
+ gdb_assert (!should_resume);
+ }
+ return;
+
+ case ZE_THREAD_STATE_PAUSED:
+ resume_single_thread (tp);
+ return;
+ }
+ });
+ }
+
+ /* Resume entire devices at once. */
+ for (ze_device_info *device : devices_to_resume)
+ {
+ gdb_assert (device != nullptr);
+
+ /* Skip devices we're not modeling as processes. */
+ if (device->process == nullptr)
+ continue;
+
+ resume (*device);
+ }
+}
+
+void
+ze_target::ack_library (process_info *process, const char *name)
+{
+ /* All libraries are in-memory. */
+ warning (_("unexpected acknowledgement requested for library %s."), name);
+}
+
+void
+ze_target::ack_in_memory_library (process_info *process,
+ CORE_ADDR begin, CORE_ADDR end)
+{
+ gdb_assert (process != nullptr);
+
+ process_info_private *zeproc = process->priv;
+ gdb_assert (zeproc != nullptr);
+
+ /* The only reason why we would not have a device is if we got detached.
+
+ There is nothing to acknowledge in that case. */
+ ze_device_info *device = zeproc->device;
+ if (device == nullptr)
+ {
+ dprintf ("[%s;%s) device not found.", core_addr_to_string_nz (begin),
+ core_addr_to_string_nz (end));
+ return;
+ }
+
+ events_t &events = device->ack_pending;
+ events_t::iterator it
+ = std::find_if (events.begin (), events.end (),
+ [begin, end] (const zet_debug_event_t &ev)
+ {
+ return ((ev.type == ZET_DEBUG_EVENT_TYPE_MODULE_LOAD)
+ && (ev.info.module.moduleBegin == begin)
+ && (ev.info.module.moduleEnd == end));
+ });
+
+ if (it == events.end ())
+ {
+ dprintf ("[%s;%s) not found.", core_addr_to_string_nz (begin),
+ core_addr_to_string_nz (end));
+ return;
+ }
+
+ ze_ack_event (*device, *it);
+ events.erase (it);
+
+ dprintf ("[%s;%s) acknowledged.", core_addr_to_string_nz (begin),
+ core_addr_to_string_nz (end));
+}
diff --git a/gdbserver/ze-low.h b/gdbserver/ze-low.h
new file mode 100644
index 0000000000000000000000000000000000000000..9f4ce9a1990522e5c09eaa7918708460d0947beb
--- /dev/null
+++ b/gdbserver/ze-low.h
@@ -0,0 +1,496 @@
+/* Target interface for Level-Zero based targets for gdbserver.
+ See https://github.com/oneapi-src/level-zero.git.
+
+ Copyright (C) 2020-2025 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);
+
+/* Return the device for THREAD. */
+
+extern ze_device_info *ze_thread_device (const thread_info *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::string &args) override;
+
+ int attach (int pid) override;
+ int detach (process_info *proc) override;
+
+ int kill (process_info *proc) override;
+ void mourn (process_info *proc) override;
+ void join (int pid) override;
+
+ void resume (thread_resume *resume_info, size_t n) override;
+ ptid_t wait (ptid_t ptid, target_waitstatus *status,
+ target_wait_flags options) override;
+
+ void fetch_registers (regcache *regcache, int regno) override;
+ void store_registers (regcache *regcache, int regno) override;
+
+ int read_memory (CORE_ADDR memaddr, unsigned char *myaddr,
+ int len) override;
+
+ int write_memory (CORE_ADDR memaddr, const unsigned char *myaddr,
+ int len) override;
+
+ /* 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] 92+ messages in thread* [PATCH v3 36/44] testsuite, sycl: add SYCL support
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (34 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 35/44] gdbserver, ze, intelgt: introduce ze-low and intel-ze-low targets Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 37/44] testsuite, sycl: add test for backtracing inside a kernel Tankut Baris Aktemur
` (10 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
Add lib files, board file, and basic test scenarios for SYCL. The
scenarios test the ability to define and hit breakpoints inside and
outside a SYCL kernel. The test cases are skipped if it is found out
that the compiler does not support SYCL.
Here is a sample command to run all SYCL tests on the CPU target
device using the Intel® oneAPI DPC++/C++ Compiler:
$ make check TESTS="gdb.sycl/*.exp" \
RUNTESTFLAGS="OFFLOAD_DEVICE_GROUP=cpu CXX_FOR_TARGET=icpx"
Alternatively, the intel-offload board file can be used. This can be
more practical to pick the right compilers.
$ make check TESTS="gdb.sycl/*.exp" \
RUNTESTFLAGS="OFFLOAD_DEVICE_GROUP=cpu --target_board='intel-offload'"
Running the GPU tests requires that a gdbserver executable built for
the intelgt target exists in the path and its name is
'gdbserver-intelgt'.
Co-authored-by: Abdul Basit Ijaz <abdul.b.ijaz@intel.com>
Co-authored-by: Stephan Rohr <stephan.rohr@intel.com>
---
gdb/testsuite/README | 9 +
gdb/testsuite/boards/intel-offload.exp | 36 +++
gdb/testsuite/gdb.sycl/break.exp | 63 +++++
gdb/testsuite/gdb.sycl/break2.exp | 66 ++++++
gdb/testsuite/gdb.sycl/single-task.cpp | 50 ++++
gdb/testsuite/lib/gdb.exp | 17 +-
gdb/testsuite/lib/intelgt-utils.exp | 43 ++++
gdb/testsuite/lib/sycl-devices.cpp | 107 +++++++++
gdb/testsuite/lib/sycl-hello.cpp | 43 ++++
gdb/testsuite/lib/sycl-util.cpp | 135 +++++++++++
gdb/testsuite/lib/sycl.exp | 410 +++++++++++++++++++++++++++++++++
11 files changed, 978 insertions(+), 1 deletion(-)
diff --git a/gdb/testsuite/README b/gdb/testsuite/README
index c5d429820048560d07bd38a1a3708dea2c43210d..5664965799ccc9016152aa5b544ad31a10c704e8 100644
--- a/gdb/testsuite/README
+++ b/gdb/testsuite/README
@@ -354,6 +354,15 @@ by switching to a different user on the same machine. These users
will have random files copied into their $HOME directories, so it is a
good idea to setup new users just for this purpose.
+OFFLOAD_DEVICE_GROUP
+
+This option can be used to restrict the offloading tests to run only
+on a specific group/family of devices. Multiple target device groups
+can be selected by passing a comma separated list. By default, it is
+set to "cpu,gpu,accelerator". Example use:
+
+ make check RUNTESTFLAGS='OFFLOAD_DEVICE_GROUP="gpu,cpu"'
+
Testing All Simple Boards
*************************
diff --git a/gdb/testsuite/boards/intel-offload.exp b/gdb/testsuite/boards/intel-offload.exp
new file mode 100644
index 0000000000000000000000000000000000000000..504efec68f4856345bb022f5b46ebbae5956a49a
--- /dev/null
+++ b/gdb/testsuite/boards/intel-offload.exp
@@ -0,0 +1,36 @@
+# Copyright 2022-2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# This file is a dejagnu "board file" and is used for running the
+# SYCL testsuite.
+#
+# Example usage:
+# bash$ make check TESTS="gdb.sycl/*.exp" RUNTESTFLAGS='--target_board=intel-offload'
+
+load_generic_config "unix"
+process_multilib_options ""
+load_board_description "local-board"
+
+set gdb_test_timeout 100
+
+unset_board_info isremote
+set_board_info isremote 0
+
+set_board_info compiler "icx"
+set_board_info c++compiler "icpx"
+set_board_info f90compiler "ifx"
+
+puts "Info: Using timeout value $gdb_test_timeout"
+puts "Info: Using C++ compiler icpx and Fortran compiler ifx"
diff --git a/gdb/testsuite/gdb.sycl/break.exp b/gdb/testsuite/gdb.sycl/break.exp
new file mode 100644
index 0000000000000000000000000000000000000000..63efc51db0a79bb1fc566e2b842fa597fe4381f5
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/break.exp
@@ -0,0 +1,63 @@
+# Copyright 2019-2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Tests GDB's support for SYCL; in particular, inserting and hitting
+# breakpoints inside and outside a kernel.
+
+load_lib sycl.exp
+
+standard_testfile single-task.cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+ unsupported "target does not support SYCL"
+ return 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+ "${binfile}" $srcfile {sycl debug}]} {
+ return -1
+}
+
+foreach device $sycl_device_list {
+ sycl_with_intelgt_lock $device {
+ clean_restart "${binfile}"
+
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ # Set breakpoints inside and outside the kernel.
+ array set bp_locations_kernel {}
+ set num_kernel_locations 4
+
+ gdb_breakpoint "$srcfile:[gdb_get_line_number line-after-kernel]" \
+ {message}
+
+ for {set i 1} {$i <= $num_kernel_locations} {incr i} {
+ set bp_locations_kernel($i) [gdb_get_line_number "kernel-line-$i"]
+ gdb_breakpoint "$srcfile:$bp_locations_kernel($i)" {message}
+ }
+
+ # Test that we actually hit the breakpoints.
+ for {set i 1} {$i <= $num_kernel_locations} {incr i} {
+ gdb_continue_to_breakpoint "kernel line $i" \
+ ".*$srcfile:$bp_locations_kernel($i).*"
+ }
+
+ gdb_continue_to_breakpoint "line after kernel" \
+ ".*$srcfile:[gdb_get_line_number line-after-kernel].*"
+ }
+}
diff --git a/gdb/testsuite/gdb.sycl/break2.exp b/gdb/testsuite/gdb.sycl/break2.exp
new file mode 100644
index 0000000000000000000000000000000000000000..7eafd115a77fd30b58b050e16deb8e5f64e204b6
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/break2.exp
@@ -0,0 +1,66 @@
+# Copyright 2019-2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Tests GDB's support for SYCL; in particular, inserting and hitting
+# breakpoints inside and outside a kernel. This test is similar to
+# break.exp except that not all the breakpoints are defined at the
+# beginning. A couple in- and out-kernel breakpoints are defined when
+# we are inside the kernel.
+
+load_lib sycl.exp
+
+standard_testfile single-task.cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+ unsupported "target does not support SYCL"
+ return 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+ "${binfile}" $srcfile {sycl debug}]} {
+ return -1
+}
+
+foreach device $sycl_device_list {
+ sycl_with_intelgt_lock $device {
+ clean_restart "${binfile}"
+
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ # Set breakpoints inside and outside the kernel.
+ set after_kernel [gdb_get_line_number "line-after-kernel"]
+ set inside_kernel_1 [gdb_get_line_number "kernel-line-1"]
+ set inside_kernel_2 [gdb_get_line_number "kernel-line-2"]
+ set inside_kernel_3 [gdb_get_line_number "kernel-line-3"]
+ set inside_kernel_4 [gdb_get_line_number "kernel-line-4"]
+
+ gdb_breakpoint $inside_kernel_2 {message}
+ gdb_continue_to_breakpoint "kernel line 2" \
+ ".*$srcfile:$inside_kernel_2.*"
+
+ # Now we are inside the kernel and defining a kernel breakpoint.
+ gdb_breakpoint $inside_kernel_4 {message}
+ gdb_continue_to_breakpoint "kernel line 4" \
+ ".*$srcfile:$inside_kernel_4.*"
+
+ # We are still inside the kernel and defining a host breakpoint.
+ gdb_breakpoint $after_kernel {message}
+ gdb_continue_to_breakpoint "line after kernel" \
+ ".*$srcfile:$after_kernel.*"
+ }
+}
diff --git a/gdb/testsuite/gdb.sycl/single-task.cpp b/gdb/testsuite/gdb.sycl/single-task.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ea7aace4203024064f1b7ac71bf0b5a7a3b480ee
--- /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-2025 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#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 8d94d6b81af3817a14b6d523cccc4e9eb3ba756c..594bf855b9a7d936ee5d06a81f45a9ad7fe386bd 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -6646,6 +6646,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
@@ -8883,7 +8898,7 @@ proc build_executable_from_specs {testname executable options args} {
set binfile [standard_output_file $executable]
set func gdb_compile
- set func_index [lsearch -regexp $options {^(pthreads|shlib|shlib_pthreads|openmp)$}]
+ set func_index [lsearch -regexp $options {^(pthreads|shlib|shlib_pthreads|openmp|sycl)$}]
if {$func_index != -1} {
set func "${func}_[lindex $options $func_index]"
}
diff --git a/gdb/testsuite/lib/intelgt-utils.exp b/gdb/testsuite/lib/intelgt-utils.exp
new file mode 100644
index 0000000000000000000000000000000000000000..94f16e3065f1ffd67f91d7adba50c220a176fd29
--- /dev/null
+++ b/gdb/testsuite/lib/intelgt-utils.exp
@@ -0,0 +1,43 @@
+# Copyright 2020-2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Intelgt utility procedures.
+
+# The lock file used to ensure that only one GDB has access to the GPU
+# at a time.
+
+set intelgt_lock_filename intelgt-parallel.lock
+
+# Return non-zero if the instruction at ADDRESS is compact, 0 otherwise.
+
+proc is_compact_insn {address} {
+ # Check the CmptCtrl flag (bit 29).
+ set test "is compact insn"
+ set is_compact [get_integer_valueof \
+ "((unsigned char *)${address})\[3\] & 0x20" 0 $test]
+ return $is_compact
+}
+
+# Set the breakpoint bit of the instruction at ADDRESS.
+
+proc_with_prefix set_breakpoint_bit {address} {
+ # Set Bit 7 on a compacted instruction, Bit 30 on a full instruction.
+ set test "set bp bit"
+ if {[is_compact_insn $address]} {
+ gdb_test "print/x ((unsigned char *)${address})\[0\] |= 0x80" "" $test
+ } else {
+ gdb_test "print/x ((unsigned char *)${address})\[3\] |= 0x40" "" $test
+ }
+}
diff --git a/gdb/testsuite/lib/sycl-devices.cpp b/gdb/testsuite/lib/sycl-devices.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a307c3d6be8a45932e42abae42582f4b59a34488
--- /dev/null
+++ b/gdb/testsuite/lib/sycl-devices.cpp
@@ -0,0 +1,107 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2022-2025 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Utility file for SYCL test programs to get list of available devices. */
+
+#include <sycl/sycl.hpp>
+#include <map>
+
+static std::string
+get_backend_name (sycl::backend backend_arg)
+{
+ std::string backend_name;
+
+ if (backend_arg == sycl::backend::opencl)
+ backend_name = "opencl";
+ else if (backend_arg == sycl::backend::ext_oneapi_level_zero)
+ backend_name = "ext_oneapi_level_zero";
+ else
+ {
+ std::cout << "SYCL: Unrecognized backend." << std::endl;
+ exit (1);
+ }
+
+ return backend_name;
+}
+
+static std::string
+get_device_type (sycl::info::device_type type)
+{
+ std::string type_name;
+
+ if (type == sycl::info::device_type::cpu)
+ type_name = "cpu";
+ else if (type == sycl::info::device_type::gpu)
+ type_name = "gpu";
+ else if (type == sycl::info::device_type::accelerator)
+ type_name = "accelerator";
+ else
+ {
+ std::cout << "SYCL: Unrecognized device type." << std::endl;
+ exit (1);
+ }
+
+ return type_name;
+}
+
+int
+main ()
+{
+ const std::vector<sycl::device> devices
+ = sycl::device::get_devices (sycl::info::device_type::all);
+
+ if (devices.empty ())
+ {
+ std::cout << "SYCL: Could not find any device" << std::endl;
+ exit (1);
+ }
+
+ std::map<std::string, int> device_types;
+
+ for (const sycl::device &device : devices)
+ {
+ const std::string dev_name
+ = device.get_info<sycl::info::device::name> ();
+ const std::string version
+ = device.get_info<sycl::info::device::driver_version> ();
+ const std::string backend_name
+ = get_backend_name (device.get_backend ());
+
+ const std::string type
+ = get_device_type (device.get_info<sycl::info::device::device_type> ());
+
+ if (backend_name == "")
+ continue;
+
+ std::string dev_key {dev_name + ";" + backend_name + ";" + version
+ + ";" + type};
+ device_types[dev_key]++;
+ }
+
+ std::cout << "SYCL: List of Target devices: [";
+ int index = 0;
+ for (const auto& [dev_key, count] : device_types)
+ {
+ index++;
+ std::cout << dev_key << ";" << count;
+ if (index < device_types.size ())
+ std::cout << ",";
+ }
+ std::cout << "]" << std::endl;
+
+ return 0;
+}
diff --git a/gdb/testsuite/lib/sycl-hello.cpp b/gdb/testsuite/lib/sycl-hello.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5586b3b16a5bf25950c9a20e5543568dde7f391b
--- /dev/null
+++ b/gdb/testsuite/lib/sycl-hello.cpp
@@ -0,0 +1,43 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2019-2025 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <sycl/sycl.hpp>
+#include "sycl-util.cpp"
+
+static int numbers[8];
+
+int
+main (int argc, char *argv[])
+{
+ sycl::queue deviceQueue {get_sycl_queue (argc, argv)};
+ sycl::range<1> length {8};
+
+ { /* Extra scope enforces waiting on the kernel. */
+ sycl::buffer<int, 1> buf {numbers, length};
+ deviceQueue.submit ([&] (sycl::handler& cgh)
+ {
+ auto accessor = buf.get_access<sycl::access::mode::read_write> (cgh);
+
+ cgh.parallel_for<class SyclHello> (length, [=] (sycl::id<1> wiID)
+ {
+ accessor[wiID] = wiID[0] + 1; /* inside-kernel */
+ });
+ });
+ }
+
+ return 0;
+}
diff --git a/gdb/testsuite/lib/sycl-util.cpp b/gdb/testsuite/lib/sycl-util.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8c5b89df7612e1d1ef1c913d9aebf88e7d485f29
--- /dev/null
+++ b/gdb/testsuite/lib/sycl-util.cpp
@@ -0,0 +1,135 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2019-2025 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Utility file for SYCL test programs to enable explicit selection of
+ a SYCL device. Include this file in each SYCL test program. */
+
+#include <sycl/sycl.hpp>
+#include <iostream>
+#include <vector>
+
+static sycl::info::device_type
+get_device_type (const std::string &type_arg)
+{
+ sycl::info::device_type type;
+
+ if (type_arg.compare ("cpu") == 0)
+ type = sycl::info::device_type::cpu;
+ else if (type_arg.compare ("gpu") == 0)
+ type = sycl::info::device_type::gpu;
+ else if (type_arg.compare ("accelerator") == 0)
+ type = sycl::info::device_type::accelerator;
+ else
+ {
+ std::cout << "SYCL: Unrecognized device type '"
+ << type_arg << "'" << std::endl;
+ exit (1);
+ }
+
+ return type;
+}
+
+static sycl::backend
+get_backend_type (const std::string &backend_arg)
+{
+ sycl::backend backend;
+
+ if (backend_arg.compare ("opencl") == 0)
+ backend = sycl::backend::opencl;
+ else if (backend_arg.compare ("ext_oneapi_level_zero") == 0
+ || backend_arg.compare ("level_zero") == 0)
+ backend = sycl::backend::ext_oneapi_level_zero;
+ else
+ {
+ std::cout << "SYCL: Unrecognized backend '"
+ << backend_arg << "'" << std::endl;
+ exit (1);
+ }
+
+ return backend;
+}
+
+static std::vector<sycl::device>
+get_sycl_devices (int argc, char *argv[])
+{
+ if (argc <= 3)
+ {
+ std::cout << "Usage: " << argv[0]
+ << " <cpu|gpu|accelerator>"
+ << " <device name substring>"
+ << " <backend name opencl|level_zero>" << std::endl;
+ exit (1);
+ }
+
+ std::string type_arg {argv[1]};
+ std::string name_arg {argv[2]};
+ std::string backend_arg {argv[3]};
+
+ sycl::info::device_type type = get_device_type (type_arg);
+ sycl::backend backend_type = get_backend_type (backend_arg);
+
+ std::vector<sycl::device> devices = sycl::device::get_devices (type);
+
+ std::vector<sycl::device> filtered_devices;
+ for (const sycl::device &device : devices)
+ {
+ std::string dev_name = device.get_info<sycl::info::device::name> ();
+ std::string platform_name
+ = device.get_platform ().get_info<sycl::info::platform::name> ();
+ std::string version
+ = device.get_info<sycl::info::device::driver_version> ();
+ sycl::backend backend = device.get_backend ();
+
+ if (dev_name.find (name_arg) != std::string::npos
+ && backend == backend_type)
+ filtered_devices.push_back (device);
+ }
+
+ if (filtered_devices.empty ())
+ {
+ std::cout << "SYCL: Could not select a device" << std::endl;
+ exit (1);
+ }
+
+ return filtered_devices;
+}
+
+static void
+print_device (const sycl::device &device)
+{
+ std::string dev_name
+ = device.get_info<sycl::info::device::name> ();
+ std::string platform_name
+ = device.get_platform ().get_info<sycl::info::platform::name> ();
+ std::string version
+ = device.get_info<sycl::info::device::driver_version> ();
+
+ std::cout << "[" << dev_name << "]"
+ << " from [" << platform_name << "]"
+ << " version [" << version << "]";
+}
+
+static sycl::queue
+get_sycl_queue (int argc, char *argv[])
+{
+ sycl::device device = get_sycl_devices (argc, argv)[0];
+ std::cout << "SYCL: Using device: ";
+ print_device (device);
+ std::cout << std::endl;
+
+ return sycl::queue {device}; /* return-sycl-queue */
+}
diff --git a/gdb/testsuite/lib/sycl.exp b/gdb/testsuite/lib/sycl.exp
new file mode 100644
index 0000000000000000000000000000000000000000..0c85b7742b9c630d00b20f24d2924142b1dfaa60
--- /dev/null
+++ b/gdb/testsuite/lib/sycl.exp
@@ -0,0 +1,410 @@
+# Copyright 2019-2025 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Support library for testing SYCL GDB features
+#
+# A particular SYCL device can be selected by passing the SYCL program
+# three command-line arguments:
+# 1. the device type, whose value is in {cpu, gpu, accelerator}.
+# 2. a substring of the device name.
+# 3. backend name.
+#
+# To set these arguments properly, use a SYCL board file, and
+# make your test program select a queue via the get_sycl_queue
+# function in gdb.sycl/sycl-util.cpp. See gdb.sycl/sycl-hello.cpp
+# for a sample SYCL program setup.
+
+load_lib intelgt-utils.exp
+
+# Define global variables for the driver version and platform.
+
+if {![info exists OFFLOAD_DEVICE_GROUP]} {
+ set OFFLOAD_DEVICE_GROUP "cpu,gpu,accelerator"
+}
+
+verbose -log "OFFLOAD_DEVICE_GROUP is '$OFFLOAD_DEVICE_GROUP'"
+
+# Return true if the SYCL device selected via the board file
+# matches the arguments. Otherwise return false.
+# Input arg DEVICE contains ";" separated following information:
+# Device name;Backend Type;Backend Platform version.
+# See an example in init_sycl_devices_list.
+
+proc require_sycl_device {device type name} {
+ set args_list [sycl_get_device_args $device]
+ if {[llength $args_list] <= 2} {
+ return 0
+ }
+
+ set type_match [expr {[lindex $args_list 0] eq $type}]
+ set name_match [string match $name [lindex $args_list 1]]
+
+ return [expr $type_match && $name_match]
+}
+
+# Run a test on the target to check if it recognizes SYCL.
+# Remove device from the available devices list if SYCL is not supported
+# and return the updated list.
+
+proc get_sycl_supported_devices {sycl_device_list} {
+ global srcdir inferior_exited_re
+
+ set supported_sycl_device_list {}
+
+ # Set up, compile, and execute a simple SYCL program.
+ set exe [standard_output_file sycl-hello]
+ set src "$srcdir/lib/sycl-hello.cpp"
+
+ if {[build_executable "failed to compile $src" \
+ $exe $src {sycl debug}]} {
+ verbose "SYCL: Compilation failed" 0
+ return 1
+ }
+ verbose -log "\nSYCL: Compilation succeeded"
+
+ foreach device $sycl_device_list {
+ if ![is_sycl_device_filtered $device] {
+ continue
+ }
+
+ clean_restart "${exe}"
+
+ if {![sycl_start $device]} {
+ verbose "SYCL: Support not detected for ${device}" 0
+ gdb_exit
+ continue
+ }
+
+ set inside_kernel [gdb_get_line_number "inside-kernel" $src]
+ gdb_breakpoint "sycl-hello.cpp:$inside_kernel"
+
+ set result 1
+ sycl_with_intelgt_lock $device {
+ gdb_test_multiple "continue" "continue" {
+ -re -wrap "$inferior_exited_re normally].*" {
+ set result 1
+ }
+ -re -wrap "$inferior_exited_re with code.*" {
+ set result 1
+ }
+ -re -wrap "(?:Breakpoint) .* (at|in).*sycl-hello.cpp:$inside_kernel.*" {
+ set result 0
+ }
+ -re -wrap "received signal SIGABRT, Aborted.*" {
+ set result 1
+ }
+ }
+ }
+
+ if {$result == 0} {
+ verbose "SYCL: Support detected for ${device}" 0
+ lappend supported_sycl_device_list "${device}"
+ } else {
+ verbose "SYCL: Support not detected for ${device}" 0
+ }
+
+ gdb_exit
+ }
+
+ return $supported_sycl_device_list
+}
+
+# Run the program under debug by passing DEVICE as the command line
+# argument. If the device is not Intel GT, stop at main. If the
+# device is Intel GT, continue until the zeContextCreate API call and
+# attempt to create another inferior connected to an Intel GT
+# gdbserver target.
+#
+# Return 1 on success, 0 on failure.
+
+proc sycl_start {device} {
+ if {[require_sycl_device $device "gpu" "Intel*"]} {
+ # To debug an Intel GT device, we create an additional
+ # inferior. For the multi-target setting to work, we
+ # need to operate in target-non-stop mode.
+ gdb_test_no_output "maint set target-non-stop on"
+ }
+
+ set args ""
+ foreach arg [sycl_get_device_args $device] {
+ append args "'$arg' "
+ }
+ gdb_test_no_output "set args $args"
+
+ if {![runto_main]} {
+ return 0
+ }
+
+ if {[require_sycl_device $device "gpu" "Intel*"]} {
+ # For the Intel GT target, define a "hook" BP at a
+ # Level-Zero API function at which the Level-Zero backend
+ # would have been already initialized, attaching to the
+ # device would be expected to succeed. Once we hit the
+ # hook BP, we create the additional inferior.
+ gdb_breakpoint "zeContextCreate" {*}"allow-pending temporary"
+ set hook_bp_hit [gdb_continue_to_breakpoint "hook bp" \
+ "zeContextCreate\[^\r\n\]*"]
+ if {$hook_bp_hit != 0} {
+ return 0
+ }
+
+ with_test_prefix "$device" {
+ set inf_pid [get_inferior_pid]
+ gdb_test "add-inferior -no-connection"
+ gdb_test "inferior 2"
+ set cmd "gdbserver-intelgt --once --attach - $inf_pid"
+ gdb_test "target remote | $cmd" \
+ "Remote debugging.*" "connect the remote target"
+ gdb_test "inferior 1"
+ gdb_test "set schedule-multi on"
+ gdb_test "info inferior" ".*" "inferiors for logging"
+ }
+ }
+
+ return 1
+}
+
+# Get list of devices and return 0 if device list is non-empty else
+# return 1. Each device entry of this list contains ";" separated
+# following information:
+# Device name;Backend Type;Backend Platform version.
+# e.g.
+# Intel(R) ..;ext_oneapi_level_zero;1.3.24347
+
+gdb_caching_proc init_sycl_devices_list {} {
+ global srcdir
+ global inferior_exited_re
+ global sycl_device_list
+
+ set sycl_device_list {}
+ set supported_sycl_device_list {}
+
+ # Set up, compile, and execute a simple SYCL program.
+ set exe [standard_output_file sycl-devices]
+ set src "$srcdir/lib/sycl-devices.cpp"
+
+ if {![test_compiler_info {icx-*} c++]} {
+ unsupported "SYCL tests supported only for dpcpp and icpx compilers"
+ return $sycl_device_list
+ }
+
+ if {[prepare_for_testing "failed to compile $src" \
+ $exe $src {sycl debug}]} {
+ verbose "SYCL: Compilation failed" 0
+ return $sycl_device_list
+ }
+ verbose -log "\nSYCL: Compilation succeeded"
+
+ if {![runto_main]} {
+ untested "failed to run sycl-devices to main"
+ return $sycl_device_list
+ }
+
+ set result 1
+ gdb_test_multiple "continue" "continue" {
+ -re "SYCL: List of Target devices: \\\[(\[^\r\n\]+)\\\]" {
+ set sycl_device_list [split $expect_out(1,string) ","]
+ exp_continue
+ }
+ -re -wrap "$inferior_exited_re normally].*" {
+ set result 0
+ }
+ -re -wrap "$inferior_exited_re with code.*" {
+ set result 1
+ }
+ }
+
+ set supported_sycl_device_list [get_sycl_supported_devices $sycl_device_list]
+ if {($result == 0) && ([llength $supported_sycl_device_list] > 0)} {
+ verbose "SYCL: Devices found: $supported_sycl_device_list" 0
+ } else {
+ set result 1
+ verbose "SYCL: No device found" 0
+ }
+
+ gdb_exit
+
+ return $supported_sycl_device_list
+}
+
+# Return the ID of the current thread (<inferior number>.<thread
+# number>). This procedure can be more practical than using the
+# $_thread and $_inferior convenience variables, because if the SYCL
+# kernel is offloaded to a CPU target, the current thread would be a
+# single integer, but if offloaded to a GPU, it may be an
+# inferior-qualified number like N.M.
+proc get_current_thread {location} {
+ global decimal
+
+ gdb_test_multiple "thread" "get current thread at $location" {
+ -re -wrap "Current thread is ($decimal|$decimal\.$decimal).*" {
+ pass $gdb_test_name
+ return $expect_out(1,string)
+ }
+ -re -wrap "" {
+ fail $gdb_test_name
+ }
+ }
+ return 0
+}
+
+# Returns 1 if the target device is selected via OFFLOAD_DEVICE_GROUP
+# and 0 otherwise.
+# DEVICE contains ";" separated following information:
+# Device name;Backend Type;Backend Platform version.
+# See an example in init_sycl_devices_list.
+
+proc is_sycl_device_filtered {device} {
+ global OFFLOAD_DEVICE_GROUP
+
+ # Filter according to OFFLOAD_DEVICE_GROUP.
+ set device_info [split "$device" ";"]
+ set backend [lindex $device_info 1]
+ set device_type [lindex $device_info 3]
+
+ if {[lsearch -nocase [split $OFFLOAD_DEVICE_GROUP ","] $device_type] < 0} {
+ verbose -log "SYCL: device type $device_type is unwanted, skipping '$device'"
+ return 0
+ }
+
+ if {$device_type == "gpu"
+ && [string match -nocase "*opencl*" $backend]} {
+ verbose -log "SYCL: unsupported combination: $device_type & $backend"
+ return 0
+ }
+
+ return 1
+}
+
+# Returns number of devices found in device string.
+
+proc sycl_get_device_count {device} {
+ set device_info [split "$device" ";"]
+ set device_count [lindex $device_info 4]
+ return $device_count
+}
+
+# Gets the list of args required for running the SYCL tests, where input device
+# contains ";" separated following information:
+# Device name;Backend Type;Backend Platform version;Device Type;count.
+# See an example in init_sycl_devices_list.
+
+proc sycl_get_device_args {device} {
+ global hex
+
+ set device_info [split "$device" ";"]
+ set sycl_driver_platform [lindex $device_info 1]
+ set sycl_driver_version [lindex $device_info 2]
+ set device_type [lindex $device_info 3]
+ set device_name ""
+ set args_list {}
+
+ if {$device_type eq "gpu"} {
+ lappend args_list "gpu"
+ lappend args_list [lindex $device_info 0]
+ } elseif {$device_type eq "cpu"} {
+ lappend args_list "cpu"
+ if {[string match "*Intel*" $device]} {
+ lappend args_list "Intel"
+ }
+ } elseif {$device_type eq "accelerator"} {
+ lappend args_list "accelerator"
+ if {[string match "*Intel*" $device]} {
+ lappend args_list "Intel"
+ }
+ } else {
+ verbose "SYCL: Unexpected device type: ${device_type}" 0
+ }
+ lappend args_list $sycl_driver_platform
+ lappend args_list $sycl_driver_version
+ return $args_list
+}
+
+# Gets the prefix string required for the SYCL tests.
+# DEVICE contains ";" separated following information:
+# Device name;Backend Type;Backend Platform version;Device type.
+# e.g.
+# Intel(R) ...;ext_oneapi_level_zero;1.3.24347;gpu
+# Function returns ":" separated test prefix which has following info:
+# In case of non GPU device: Device type:Backend type:cpp
+# and in case of GPU: Device type GPU: Backend type: Graphics device ID
+# e.g. gpu:opencl:{0x1234}
+
+proc sycl_get_device_prefix {device} {
+ global hex
+ set args_list [sycl_get_device_args $device]
+
+ if {[string match -nocase "*Graphics*" $device]
+ || [string match -nocase "*GPU*" $device]} {
+ # In case of GPU device, add device ID to the prefix to get a unique
+ # test name for multi GPU test machines.
+ return "[lindex $args_list 0]:[lindex $args_list 2]:\
+ {[regexp -all -inline $hex [lindex $args_list 1]]}"
+ }
+ return "[lindex $args_list 0]:[lindex $args_list 2]:cpp"
+}
+
+# Run BODY under the lock, if DEVICE is an Intel GPU. Also calls
+# gdb_exit before releasing the GPU lock.
+#
+# See the similar 'with_gpu_lock' in rocm.exp.
+
+proc sycl_with_intelgt_lock {device body} {
+ with_test_prefix [sycl_get_device_prefix $device] {
+ if {![require_sycl_device "$device" "gpu" "Intel*"]} {
+ set code [catch {uplevel 1 $body} result]
+ } else {
+ with_lock $::intelgt_lock_filename {
+ set code [catch {uplevel 1 $body} result]
+ }
+ }
+
+ # In case BODY returned early due to some testcase failing.
+ gdb_exit
+ }
+
+ if {$code == 1} {
+ return -code $code -errorinfo $::errorInfo \
+ -errorcode $::errorCode $result
+ } else {
+ return -code $code $result
+ }
+}
+
+# Get the namespace version for the SYCL header corresponding to the compiler
+# used. Return 0 for older compilers using SYCL without namespace versioning.
+
+proc get_sycl_header_version {} {
+ if {[test_compiler_info {icx-202[3-9]-*} c++]} {
+ return 1
+ }
+
+ return 0
+}
+
+# Spawn a SYCL program targeting DEVICE.
+
+proc spawn_sycl_proc {executable device} {
+ # We directly use 'remote_spawn' to be able to pass
+ # the program arguments.
+ set command [list $executable]
+ foreach arg [sycl_get_device_args $device] {
+ lappend command $arg
+ }
+ verbose -log "command: $command"
+
+ set spawn_id [remote_spawn target $command]
+ return $spawn_id
+}
--
2.34.1
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread* [PATCH v3 37/44] testsuite, sycl: add test for backtracing inside a kernel
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (35 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 36/44] testsuite, sycl: add SYCL support Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 38/44] testsuite, sycl: add test for 'info locals' and 'info args' Tankut Baris Aktemur
` (9 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
Add SYCL test for checking the call stack inside a kernel, including
inlined functions.
Co-authored-by: Natalia Saiapova <natalia.saiapova@intel.com>
---
gdb/testsuite/gdb.sycl/call-stack.cpp | 92 +++++++++++++++++
gdb/testsuite/gdb.sycl/call-stack.exp | 179 ++++++++++++++++++++++++++++++++++
2 files changed, 271 insertions(+)
diff --git a/gdb/testsuite/gdb.sycl/call-stack.cpp b/gdb/testsuite/gdb.sycl/call-stack.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0bede8397ffd3200116a1c1c22b33961cbc0ee4e
--- /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-2025 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <sycl/sycl.hpp>
+#include <iostream>
+#include "../lib/sycl-util.cpp"
+
+int
+fourth (int x4, int y4)
+{
+ return x4 * y4; /* ordinary-fourth-loc */
+}
+
+int
+third (int x3, int y3)
+{
+ return fourth (x3 + 5, y3 * 3) + 30; /* ordinary-third-loc */
+}
+
+int
+second (int x2, int y2)
+{
+ return third (x2 + 5, y2 * 3) + 30; /* ordinary-second-loc */
+}
+
+int
+first (int x1, int y1)
+{
+ int result = second (x1 + 5, y1 * 3); /* ordinary-first-loc */
+ return result + 30; /* kernel-function-return */
+}
+
+__attribute__((always_inline))
+int
+inlined_second (int x, int y)
+{
+ return x * y; /* inlined-inner-loc */
+}
+
+__attribute__((always_inline))
+int
+inlined_first (int num1, int num2)
+{
+ int result = inlined_second (num1 + 5, num2 * 3); /* inlined-middle-loc */
+ return result + 30;
+}
+
+int
+main (int argc, char *argv[])
+{
+ int data[3] = {7, 8, 9};
+
+ { /* Extra scope enforces waiting on the kernel. */
+ sycl::queue deviceQueue {get_sycl_queue (argc, argv)};
+ sycl::buffer<int, 1> buf {data, sycl::range<1> {3}};
+
+ deviceQueue.submit ([&] (sycl::handler& cgh)
+ {
+ auto numbers = buf.get_access<sycl::access::mode::read_write> (cgh);
+
+ cgh.single_task ([=] ()
+ {
+ int ten = numbers[1] + 2;
+ int four = numbers[2] - 5;
+ int fourteen = ten + four;
+ numbers[0] = first (fourteen + 1, 3); /* ordinary-outer-loc */
+ numbers[1] = inlined_first (10, 2); /* inlined-outer-loc */
+ numbers[2] = first (3, 4); /* another-call */
+ });
+ });
+ }
+
+ std::cout << "Result is " << data[0] << " "
+ << data[1] << " " << data[2] << std::endl;
+ /* Expected: 210 120 126 */
+
+ return 0; /* end-of-program */
+}
diff --git a/gdb/testsuite/gdb.sycl/call-stack.exp b/gdb/testsuite/gdb.sycl/call-stack.exp
new file mode 100644
index 0000000000000000000000000000000000000000..96420f6f62fb80a164590fb68f75e932459bcb5f
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/call-stack.exp
@@ -0,0 +1,179 @@
+# Copyright 2019-2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Tests GDBs support for SYCL when there are function calls inside
+# the kernel.
+
+load_lib sycl.exp
+
+standard_testfile .cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+ unsupported "target does not support SYCL"
+ return 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+ "${binfile}" $srcfile {sycl debug}]} {
+ return -1
+}
+
+# Return the current line number.
+proc get_current_line {} {
+ global decimal gdb_prompt
+ gdb_test_multiple "info line" "get current line" {
+ -re "Line ($decimal).*$gdb_prompt $" {
+ pass $gdb_test_name
+ return $expect_out(1,string)
+ }
+ -re "$gdb_prompt $" {
+ fail $gdb_test_name
+ return 0
+ }
+ }
+}
+
+proc test_call_stack {device} {
+ global srcfile valnum_re decimal inferior_exited_re gdb_prompt
+
+ set fourth_loc [gdb_get_line_number "ordinary-fourth-loc"]
+ set third_loc [gdb_get_line_number "ordinary-third-loc"]
+ set second_loc [gdb_get_line_number "ordinary-second-loc"]
+ set first_loc [gdb_get_line_number "ordinary-first-loc"]
+ set outer_loc [gdb_get_line_number "ordinary-outer-loc"]
+ set inlined_inner_loc [gdb_get_line_number "inlined-inner-loc"]
+ set inlined_middle_loc [gdb_get_line_number "inlined-middle-loc"]
+ set inlined_outer_loc [gdb_get_line_number "inlined-outer-loc"]
+
+ set fill "\[^\r\n\]*"
+
+ set fourth_desc "fourth \\(x4=$fill, y4=$fill\\) at ${fill}$srcfile:$fourth_loc"
+ set third_desc "third \\(x3=$fill, y3=$fill\\) at ${fill}$srcfile:$third_loc"
+ set second_desc "second \\(x2=$fill, y2=$fill\\) at ${fill}$srcfile:$second_loc"
+ set first_desc "first \\(x1=$fill, y1=$fill\\) at ${fill}$srcfile:$first_loc"
+ set outer_desc "${fill}operator\\(\\)${fill} at ${fill}$srcfile:$outer_loc"
+ set inlined_inner_desc \
+ "inlined_second ${fill} at ${fill}$srcfile:$inlined_inner_loc"
+ set inlined_middle_desc \
+ "inlined_first ${fill} at ${fill}$srcfile:$inlined_middle_loc"
+ set inlined_outer_desc \
+ "${fill}operator\\(\\)${fill} at ${fill}$srcfile:$inlined_outer_loc"
+
+ # Test breaking on function names inside the kernel.
+ gdb_breakpoint "first"
+
+ gdb_test "continue" ".*$srcfile:$first_loc.*"
+
+ # Set breakpoint inside the kernel.
+ gdb_breakpoint "$srcfile:$fourth_loc"
+ gdb_continue_to_breakpoint "innermost-body" ".*$srcfile:$fourth_loc.*"
+
+ # Limit the backtrace to 5 frames because frame #5
+ # and beyond are implementation-specific to the SYCL runtime.
+ gdb_test "backtrace 5" [multi_line \
+ "#0${fill} $fourth_desc" \
+ "#1${fill} $third_desc" \
+ "#2${fill} $second_desc" \
+ "#3${fill} $first_desc" \
+ "#4${fill} $outer_desc.*"] \
+ "first backtrace"
+
+ # Test inlined function calls.
+ gdb_breakpoint $inlined_inner_loc
+
+ gdb_continue_to_breakpoint "inlined-body" ".*$srcfile:$inlined_inner_loc.*"
+
+ gdb_test "backtrace 3" [multi_line \
+ "#0${fill} $inlined_inner_desc" \
+ "#1${fill} $inlined_middle_desc" \
+ "#2${fill} $inlined_outer_desc.*"] \
+ "backtrace for inlined calls"
+
+ delete_breakpoints
+
+ # Now we will stop at the beginning of prologue of the fourth function
+ # and instruction step through the function until it returns back
+ # to the third.
+ gdb_breakpoint "*fourth"
+
+ gdb_test "continue" "fourth.*$srcfile.*" "continue to fourth prologue"
+ set i 0
+ set current_line [get_current_line]
+ set fourth_prologue_line $current_line
+
+ # Update description to not include arguments.
+ set third_desc "third ${fill} at ${fill}$srcfile:$third_loc"
+ set second_desc "second ${fill} at ${fill}$srcfile:$second_loc"
+ set first_desc "first ${fill} at ${fill}$srcfile:$first_loc"
+
+ # Print the current instruction and framedesc for logging purposes.
+ gdb_test "display/i \$pc"
+ gdb_test "display/x \$framedesc"
+
+ # Check the backtrace at each instruction until the return. We do not
+ # check the args here, as they might be invalid at prologue and epilogue.
+ # Also check that there are no additional messages after
+ # the backtrace except "(More stack frames follow...)".
+ while {($current_line == $fourth_prologue_line
+ || $current_line == $fourth_loc)
+ && $i < 100} {
+ with_test_prefix "iteration $i" {
+ if {[require_sycl_device "$device" "gpu" "Intel*"]} {
+ set fourth_desc "fourth ${fill} at ${fill}$srcfile:$current_line"
+ gdb_test "backtrace 6" [multi_line \
+ "#0${fill} $fourth_desc" \
+ "#1${fill} $third_desc" \
+ "#2${fill} $second_desc" \
+ "#3${fill} $first_desc" \
+ "#4${fill} main${fill}operator${fill}lambda${fill} at .*" \
+ "#5${fill}(\r\n\\(More stack frames follow\\.\\.\\.\\))?"] \
+ "backtrace in fourth"
+ } else {
+ # On CPU the backtrace in prologue might include
+ # additional RT specific frames. Do not assume any
+ # frame numbers and do a deeper backtrace. Do not
+ # expect the line number at fourth. We expect to see
+ # our frames somewhere in the middle.
+ set fourth_desc "fourth ${fill} at ${fill}$srcfile:$decimal"
+ gdb_test "backtrace 10" [multi_line \
+ "${fill} $fourth_desc" \
+ "${fill} $third_desc" \
+ "${fill} $second_desc" \
+ "${fill} $first_desc" \
+ "${fill} main${fill}operator${fill}lambda${fill} at .*"] \
+ "backtrace in fourth"
+ }
+ gdb_test "with scheduler-locking on -- stepi"
+ incr i
+ set current_line [get_current_line]
+ }
+ }
+
+ # Disable printing of PC and FRAMEDESC.
+ gdb_test "undisplay 1-2"
+}
+
+foreach device $sycl_device_list {
+ sycl_with_intelgt_lock $device {
+ clean_restart "${binfile}"
+
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ test_call_stack "$device"
+ }
+}
--
2.34.1
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread* [PATCH v3 38/44] testsuite, sycl: add test for 'info locals' and 'info args'
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (36 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 37/44] testsuite, sycl: add test for backtracing inside a kernel Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 39/44] testsuite, sycl: add tests for stepping and accessing data elements Tankut Baris Aktemur
` (8 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
Test that local variables and arguments inside a SYCL kernel can be
displayed correctly.
---
gdb/testsuite/gdb.sycl/info-locals-and-args.exp | 78 +++++++++++++++++++++++++
1 file changed, 78 insertions(+)
diff --git a/gdb/testsuite/gdb.sycl/info-locals-and-args.exp b/gdb/testsuite/gdb.sycl/info-locals-and-args.exp
new file mode 100644
index 0000000000000000000000000000000000000000..316e06c1c4a706a8763e4cf3b6f457ee6fa9d998
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/info-locals-and-args.exp
@@ -0,0 +1,78 @@
+# Copyright 2019-2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Tests GDBs support for SYCL for the "info locals" and "info args"
+# commands inside a kernel.
+
+load_lib sycl.exp
+
+standard_testfile call-stack.cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+ unsupported "target does not support SYCL"
+ return 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+ "${binfile}" $srcfile {sycl debug}]} {
+ return -1
+}
+
+foreach device $sycl_device_list {
+ sycl_with_intelgt_lock $device {
+ clean_restart "${binfile}"
+
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ set outer_loc [gdb_get_line_number "ordinary-outer-loc"]
+ set inner_loc [gdb_get_line_number "kernel-function-return"]
+ gdb_breakpoint "$outer_loc"
+ gdb_breakpoint "$inner_loc"
+
+ gdb_continue_to_breakpoint "bp 1" ".*$srcfile:$outer_loc.*"
+
+ set fill "\[^\r\n\]+"
+ set ten 0
+ set four 0
+ set fourteen 0
+ gdb_test_multiple "info locals" "info locals 1" -lbl {
+ -re "\r\nten = $fill" {
+ set ten 1
+ exp_continue
+ }
+ -re "\r\nfour = $fill" {
+ set four 1
+ exp_continue
+ }
+ -re "\r\nfourteen = $fill" {
+ set fourteen 1
+ exp_continue
+ }
+ -re -wrap "" {
+ gdb_assert { $ten == 1 && $four == 1 && $fourteen == 1 } \
+ $gdb_test_name
+ }
+ }
+
+ gdb_continue_to_breakpoint "bp 2" ".*$srcfile:$inner_loc.*"
+ gdb_test "info locals" "result = $fill.*" "info locals 2"
+ gdb_test "info args" [multi_line \
+ "x1 = $fill" \
+ "y1 = $fill"]
+ }
+}
--
2.34.1
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread* [PATCH v3 39/44] testsuite, sycl: add tests for stepping and accessing data elements
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (37 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 38/44] testsuite, sycl: add test for 'info locals' and 'info args' Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 40/44] testsuite, sycl: add test for 1-D and 2-D parallel_for kernels Tankut Baris Aktemur
` (7 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
Add SYCL test cases for
- stepping inside the kernel
- reading data elements and locals
- modifying data elements and locals
---
gdb/testsuite/gdb.sycl/step-into-function.exp | 47 ++++++++++++++++++++++++
gdb/testsuite/gdb.sycl/step.exp | 51 +++++++++++++++++++++++++++
2 files changed, 98 insertions(+)
diff --git a/gdb/testsuite/gdb.sycl/step-into-function.exp b/gdb/testsuite/gdb.sycl/step-into-function.exp
new file mode 100644
index 0000000000000000000000000000000000000000..e31023058a2da93bfd15f4287df2b19f12f93e70
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/step-into-function.exp
@@ -0,0 +1,47 @@
+# Copyright 2019-2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Tests GDBs support for stepping into a kernel function.
+
+load_lib sycl.exp
+
+standard_testfile call-stack.cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+ unsupported "target does not support SYCL"
+ return 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+ "${binfile}" $srcfile {sycl debug}]} {
+ return -1
+}
+
+foreach device $sycl_device_list {
+ sycl_with_intelgt_lock $device {
+ clean_restart "${binfile}"
+
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ set inside [gdb_get_line_number "ordinary-outer-loc"]
+ gdb_breakpoint $inside
+ gdb_continue_to_breakpoint "inside" ".*$srcfile:$inside.*"
+
+ gdb_test "step" "first .* at .*" "step into function"
+ }
+}
diff --git a/gdb/testsuite/gdb.sycl/step.exp b/gdb/testsuite/gdb.sycl/step.exp
new file mode 100644
index 0000000000000000000000000000000000000000..8cd02824ce7d576852f617ed5ca7362feaa9b446
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/step.exp
@@ -0,0 +1,51 @@
+# Copyright 2019-2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Tests GDBs support for SYCL; in particular, single-stepping the
+# source and printing values of local vars and data elements.
+
+load_lib sycl.exp
+
+standard_testfile single-task.cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+ unsupported "target does not support SYCL"
+ return 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+ "${binfile}" $srcfile {sycl debug}]} {
+ return -1
+}
+
+foreach device $sycl_device_list {
+ sycl_with_intelgt_lock $device {
+ clean_restart "${binfile}"
+
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ # Break at the first line of the kernel, then make steps.
+ set first_line [gdb_get_line_number "kernel-line-1"]
+ gdb_breakpoint $first_line
+ gdb_continue_to_breakpoint "first line" ".*$srcfile:$first_line.*"
+
+ gdb_test "next" "int four = .*" "next 1"
+ gdb_test "next" "int fourteen = .*" "next 2"
+ gdb_test "next" "numbers.0. = .*" "next 3"
+ }
+}
--
2.34.1
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread* [PATCH v3 40/44] testsuite, sycl: add test for 1-D and 2-D parallel_for kernels
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (38 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 39/44] testsuite, sycl: add tests for stepping and accessing data elements Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 41/44] testsuite, sycl: add test for scheduler-locking Tankut Baris Aktemur
` (6 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
Add SYCL test cases for a parallel_for kernel on a 1-dimensional and
2-dimensional index space.
---
gdb/testsuite/gdb.sycl/parallel-for-1D.cpp | 72 +++++++++++++++++++++++++++
gdb/testsuite/gdb.sycl/parallel-for-1D.exp | 55 +++++++++++++++++++++
gdb/testsuite/gdb.sycl/parallel-for-2D.cpp | 73 ++++++++++++++++++++++++++++
gdb/testsuite/gdb.sycl/parallel-for-2D.exp | 55 +++++++++++++++++++++
gdb/testsuite/gdb.sycl/step-parallel-for.exp | 63 ++++++++++++++++++++++++
5 files changed, 318 insertions(+)
diff --git a/gdb/testsuite/gdb.sycl/parallel-for-1D.cpp b/gdb/testsuite/gdb.sycl/parallel-for-1D.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7adc8d71488c9100e759c141448d1b54c4f3a2dd
--- /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-2025 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <sycl/sycl.hpp>
+#include <iostream>
+#include "../lib/sycl-util.cpp"
+
+static int
+get_dim (sycl::id<1> wi, int index)
+{
+ return wi[index]; /* inside-kernel-callee */
+}
+
+int
+main (int argc, char *argv[])
+{
+ constexpr size_t DIM0 = 1024;
+
+ int in[DIM0];
+ int out[DIM0];
+
+ /* Initialize the input. */
+ for (unsigned int i = 0; i < DIM0; i++)
+ in[i] = i + 123;
+
+ { /* Extra scope enforces waiting on the kernel. */
+ sycl::queue deviceQueue {get_sycl_queue (argc, argv)};
+ sycl::range<1> dataRange {DIM0};
+ sycl::buffer<int, 1> bufferIn {&in[0], dataRange};
+ sycl::buffer<int, 1> bufferOut {&out[0], dataRange};
+
+ deviceQueue.submit ([&] (sycl::handler& cgh) /* line-before-kernel */
+ {
+ auto accessorIn = bufferIn.get_access<sycl::access::mode::read> (cgh);
+ auto accessorOut
+ = bufferOut.get_access<sycl::access::mode::write> (cgh);
+
+ cgh.parallel_for<class kernel> (dataRange, [=] (sycl::id<1> wiID)
+ {
+ int dim0 = get_dim (wiID, 0); /* kernel-first-line */
+ int in_elem = accessorIn[wiID];
+ int in_elem2 = accessorIn[dim0];
+ accessorOut[wiID] = in_elem + 100; /* kernel-last-line */
+ });
+ });
+ }
+
+ /* Verify the output. */
+ for (unsigned int i = 0; i < DIM0; i++)
+ if (out[i] != in[i] + 100)
+ {
+ std::cout << "Element " << i << " is " << out[i] << std::endl;
+ return 1;
+ }
+
+ std::cout << "Correct" << std::endl; /* end-marker */
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.sycl/parallel-for-1D.exp b/gdb/testsuite/gdb.sycl/parallel-for-1D.exp
new file mode 100644
index 0000000000000000000000000000000000000000..f5a636881cbca36a9003076ff210b164d8ac9479
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/parallel-for-1D.exp
@@ -0,0 +1,55 @@
+# Copyright 2019-2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Tests GDBs support for SYCL, for a parallel_for kernel on a 1D range.
+
+load_lib sycl.exp
+
+standard_testfile .cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+ unsupported "target does not support SYCL"
+ return 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+ "${binfile}" $srcfile {sycl debug}]} {
+ return -1
+}
+
+foreach device $sycl_device_list {
+ sycl_with_intelgt_lock $device {
+ clean_restart "${binfile}"
+
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ set last_line [gdb_get_line_number "kernel-last-line"]
+ gdb_breakpoint $last_line
+
+ # Check that we hit the BP for a number of elements. We do not check
+ # for each element because the number of hits received may depend on
+ # whether the kernel was vectorized, and if so, the width of vectors.
+ # Since the data array in the test program is large, having a small
+ # number of trips here should be safe.
+
+ for {set i 1} {$i <= 5} {incr i} { with_test_prefix "trip $i" {
+ gdb_continue_to_breakpoint "hit the last line" \
+ ".*$srcfile:$last_line.*"
+ }}
+ }
+}
diff --git a/gdb/testsuite/gdb.sycl/parallel-for-2D.cpp b/gdb/testsuite/gdb.sycl/parallel-for-2D.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c0e32fc9b5409b666f90c0cad9d2d4fddd06dedb
--- /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-2025 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <sycl/sycl.hpp>
+#include <iostream>
+#include "../lib/sycl-util.cpp"
+
+int
+main (int argc, char *argv[])
+{
+ constexpr size_t DIM0 = 128;
+ constexpr size_t DIM1 = 64;
+
+ int in[DIM0][DIM1];
+ int out[DIM1][DIM0]; /* will transpose the input. */
+
+ /* Initialize the input. */
+ int val = 123;
+ for (unsigned int i = 0; i < DIM0; i++)
+ for (unsigned int j = 0; j < DIM1; j++)
+ in[i][j] = val++;
+
+ { /* Extra scope enforces waiting on the kernel. */
+ sycl::queue deviceQueue {get_sycl_queue (argc, argv)};
+ sycl::range<2> dataRangeIn {DIM0, DIM1};
+ sycl::range<2> dataRangeOut {DIM1, DIM0};
+ sycl::buffer<int, 2> bufferIn {&in[0][0], dataRangeIn};
+ sycl::buffer<int, 2> bufferOut {&out[0][0], dataRangeOut};
+
+ deviceQueue.submit ([&] (sycl::handler& cgh)
+ {
+ auto accessorIn = bufferIn.get_access<sycl::access::mode::read> (cgh);
+ auto accessorOut
+ = bufferOut.get_access<sycl::access::mode::write> (cgh);
+
+ cgh.parallel_for<class kernel> (dataRangeIn, [=] (sycl::id<2> wiID)
+ {
+ int dim0 = wiID[0]; /* kernel-first-line */
+ int dim1 = wiID[1];
+ int in_elem = accessorIn[wiID];
+ /* Negate the value, write into the transpositional location. */
+ accessorOut[dim1][dim0] = -1 * in_elem; /* kernel-last-line */
+ });
+ });
+ }
+
+ /* Verify the output. */
+ for (unsigned int i = 0; i < DIM0; i++)
+ for (unsigned int j = 0; j < DIM1; j++)
+ if (in[i][j] != -out[j][i])
+ {
+ std::cout << "Element " << j << "," << i
+ << " is " << out[j][i] << std::endl;
+ return 1;
+ }
+
+ std::cout << "Correct" << std::endl;
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.sycl/parallel-for-2D.exp b/gdb/testsuite/gdb.sycl/parallel-for-2D.exp
new file mode 100644
index 0000000000000000000000000000000000000000..5364528c03bbc996591ae5c3b52f496129ccefe6
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/parallel-for-2D.exp
@@ -0,0 +1,55 @@
+# Copyright 2019-2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Tests GDBs support for SYCL, for a parallel_for kernel on a 2D range.
+
+load_lib sycl.exp
+
+standard_testfile .cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+ unsupported "target does not support SYCL"
+ return 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+ "${binfile}" $srcfile {sycl debug}]} {
+ return -1
+}
+
+foreach device $sycl_device_list {
+ sycl_with_intelgt_lock $device {
+ clean_restart "${binfile}"
+
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ set last_line [gdb_get_line_number "kernel-last-line"]
+ gdb_breakpoint $last_line
+
+ # Check that we hit the BP for a number of elements. We do not check
+ # for each element because the number of hits received may depend on
+ # whether the kernel was vectorized, and if so, the width of vectors.
+ # Since the data array in the test program is large, having a small
+ # number of trips here should be safe.
+
+ for {set i 1} {$i <= 5} {incr i} { with_test_prefix "trip $i" {
+ gdb_continue_to_breakpoint "hit the last line" \
+ ".*$srcfile:$last_line.*"
+ }}
+ }
+}
diff --git a/gdb/testsuite/gdb.sycl/step-parallel-for.exp b/gdb/testsuite/gdb.sycl/step-parallel-for.exp
new file mode 100644
index 0000000000000000000000000000000000000000..09f1742aef72fd422d540778af884b34d12a1a29
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/step-parallel-for.exp
@@ -0,0 +1,63 @@
+# Copyright 2019-2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Tests GDBs support for SYCL, for stepping inside a parallel_for kernel.
+
+load_lib sycl.exp
+
+standard_testfile parallel-for-1D.cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+ unsupported "target does not support SYCL"
+ return 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+ "${binfile}" $srcfile {sycl debug}]} {
+ return -1
+}
+
+foreach device $sycl_device_list {
+ sycl_with_intelgt_lock $device {
+ clean_restart "${binfile}"
+
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ set first_line [gdb_get_line_number "kernel-first-line"]
+ gdb_breakpoint $first_line
+
+ # Check that we can step inside the kernel. We do not check
+ # for each element because the number of hits received may depend on
+ # whether the kernel was vectorized, and if so, the width of vectors.
+ # Since the data array in the test program is large, having a small
+ # number of trips here should be safe.
+ #
+ # Lock the scheduler for stepping to avoid inference.
+
+ gdb_test_no_output "set scheduler-locking step"
+
+ for {set i 1} {$i <= 5} {incr i} { with_test_prefix "trip $i" {
+ gdb_continue_to_breakpoint "hit the first line" \
+ ".*$srcfile:$first_line.*"
+
+ gdb_test "next" "int in_elem = .*" "next 1"
+ gdb_test "next" "int in_elem2 = .*" "next 2"
+ gdb_test "next" "accessorOut.wiID. = in_elem.*" "next 3"
+ }}
+ }
+}
--
2.34.1
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread* [PATCH v3 41/44] testsuite, sycl: add test for scheduler-locking
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (39 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 40/44] testsuite, sycl: add test for 1-D and 2-D parallel_for kernels Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 42/44] testsuite, arch, intelgt: add a disassembly test Tankut Baris Aktemur
` (5 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
Add a new test to check that scheduler-locking when inside a SYCL
kernel works fine.
---
gdb/testsuite/gdb.sycl/scheduler-locking.exp | 67 ++++++++++++++++++++++++++++
1 file changed, 67 insertions(+)
diff --git a/gdb/testsuite/gdb.sycl/scheduler-locking.exp b/gdb/testsuite/gdb.sycl/scheduler-locking.exp
new file mode 100644
index 0000000000000000000000000000000000000000..8965790ffcd50ce9c840cf23aa59a0ac4cdfbf86
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/scheduler-locking.exp
@@ -0,0 +1,67 @@
+# Copyright 2020-2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Test the scheduler-locker for resuming inside a SYCL kernel.
+
+load_lib sycl.exp
+
+standard_testfile parallel-for-1D.cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+ unsupported "target does not support SYCL"
+ return 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+ "${binfile}" $srcfile {sycl debug}]} {
+ return -1
+}
+
+foreach device $sycl_device_list {
+ sycl_with_intelgt_lock $device {
+ clean_restart "${binfile}"
+
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ set first_line [gdb_get_line_number "kernel-first-line"]
+ set second_line [expr $first_line + 1]
+ set third_line [expr $first_line + 2]
+ set fourth_line [expr $first_line + 3]
+ gdb_breakpoint $first_line
+ gdb_breakpoint $second_line
+ gdb_breakpoint $third_line
+ gdb_breakpoint $fourth_line
+
+ gdb_continue_to_breakpoint "hit the first line" \
+ ".*$srcfile:$first_line.*"
+
+ gdb_test_no_output "set scheduler-locking on"
+
+ # Resuming should make only the current thread move.
+ set current_thread [get_current_thread "line $first_line"]
+ gdb_test "continue" \
+ "Thread $current_thread \[^\r\n\]+$srcfile:$second_line.*" \
+ "hit the second line"
+ gdb_test "continue" \
+ "Thread $current_thread \[^\r\n\]+$srcfile:$third_line.*" \
+ "hit the third line"
+ gdb_test "continue" \
+ "Thread $current_thread \[^\r\n\]+$srcfile:$fourth_line.*" \
+ "hit the fourth line"
+ }
+}
--
2.34.1
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread* [PATCH v3 42/44] testsuite, arch, intelgt: add a disassembly test
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (40 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 41/44] testsuite, sycl: add test for scheduler-locking Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 43/44] testsuite, arch, intelgt: add intelgt-program-bp.exp Tankut Baris Aktemur
` (4 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
From: Albertano Caruso <albertano.caruso@intel.com>
Test the disassemble command on the Intel GT target.
---
gdb/testsuite/gdb.arch/intelgt-disassemble.exp | 82 ++++++++++++++++++++++++++
gdb/testsuite/gdb.arch/sycl-simple.cpp | 42 +++++++++++++
2 files changed, 124 insertions(+)
diff --git a/gdb/testsuite/gdb.arch/intelgt-disassemble.exp b/gdb/testsuite/gdb.arch/intelgt-disassemble.exp
new file mode 100644
index 0000000000000000000000000000000000000000..83c6ff3f0c1172705997414a4b9739eb9050220e
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/intelgt-disassemble.exp
@@ -0,0 +1,82 @@
+# Copyright 2019-2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Tests disassembly support for Intel(R) Graphics Technology.
+
+load_lib sycl.exp
+
+standard_testfile sycl-simple.cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+ unsupported "target does not support SYCL"
+ return 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+ "${binfile}" $srcfile {sycl debug}]} {
+ return -1
+}
+
+foreach device $sycl_device_list {
+ if {![require_sycl_device "$device" "gpu" "Intel*"]} {
+ unsupported "skipping device '$device', test is aimed at Intel GPUs only"
+ continue
+ }
+
+ sycl_with_intelgt_lock $device {
+ clean_restart "${binfile}"
+
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ # Set breakpoint inside the kernel.
+ set bp_location [gdb_get_line_number "inside-kernel"]
+ gdb_breakpoint "$srcfile:$bp_location"
+
+ # Hit the breakpoint.
+ gdb_continue_to_breakpoint "kernel" ".*$srcfile:$bp_location.*"
+
+ # Check if IGA is supposed to exist.
+ set has_iga 0
+ gdb_test_multiple "show configuration" "check iga" {
+ -re -wrap "with-libiga64.*" {
+ set has_iga 1
+ }
+ -re -wrap "without-libiga64.*" {
+ set has_iga 0
+ }
+ }
+
+ if {$has_iga} {
+ gdb_test "disassemble \$pc, \$pc+64" \
+ [multi_line "Dump of assembler code from $hex to $hex:" \
+ "=> $hex .*" \
+ "End of assembler dump."] \
+ "disassemble"
+ } else {
+ unsupported "disassemble"
+
+ gdb_test "disassemble \$pc, \$pc+64" \
+ [multi_line "Dump of assembler code from $hex to $hex:" \
+ "=> $hex \[^\r\n\]+" \
+ "Disassemble feature not available: libiga64 is missing." \
+ "" \
+ "unknown disassembler error.*"] \
+ "disassembly is unavailable without iga"
+ }
+ }
+}
diff --git a/gdb/testsuite/gdb.arch/sycl-simple.cpp b/gdb/testsuite/gdb.arch/sycl-simple.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5dac107dca6679adb528fa343d5e8223f86654ba
--- /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-2025 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#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] 92+ messages in thread* [PATCH v3 43/44] testsuite, arch, intelgt: add intelgt-program-bp.exp
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (41 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 42/44] testsuite, arch, intelgt: add a disassembly test Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-08-01 9:37 ` [PATCH v3 44/44] testsuite, sycl: test canceling a stepping flow Tankut Baris Aktemur
` (3 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
Introduce a new test to check if GDB can resume from a
manually-inserted program breakpoint on the Intel GT device without
repeatedly hitting the breakpoint.
---
gdb/testsuite/gdb.arch/intelgt-program-bp.exp | 83 +++++++++++++++++++++++++++
1 file changed, 83 insertions(+)
diff --git a/gdb/testsuite/gdb.arch/intelgt-program-bp.exp b/gdb/testsuite/gdb.arch/intelgt-program-bp.exp
new file mode 100644
index 0000000000000000000000000000000000000000..3bf1796acc70d7a95d09570811724a120b30052a
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/intelgt-program-bp.exp
@@ -0,0 +1,83 @@
+# Copyright 2020-2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Test that GDB steps over program breakpoints (i.e. breakpoints
+# manually inserted by the user by modifying an instruction) on an
+# Intel GT target.
+
+load_lib sycl.exp
+
+standard_testfile sycl-simple.cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+ unsupported "target does not support SYCL"
+ return 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+ "${binfile}" $srcfile {sycl debug}]} {
+ return -1
+}
+
+foreach device $sycl_device_list {
+ if {![require_sycl_device "$device" "gpu" "Intel*"]} {
+ unsupported "skipping device '$device', test is aimed at Intel GPUs only"
+ continue
+ }
+
+ sycl_with_intelgt_lock $device {
+ clean_restart "${binfile}"
+
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ set kernel_line [gdb_get_line_number "inside-kernel"]
+ # Make the BP temporary, so that GDB will remove it. We will
+ # then set it manually.
+ gdb_breakpoint $kernel_line {temporary}
+ gdb_continue_to_breakpoint "inside kernel" ".*$srcfile:$kernel_line.*"
+
+ # Manually set the BP bit on the current PC and the next one.
+ with_test_prefix "current pc" {
+ set first_pc [get_hexadecimal_valueof "\$pc" 0 "first stop pc"]
+ set_breakpoint_bit $first_pc
+ }
+
+ with_test_prefix "next pc" {
+ if {[is_compact_insn $first_pc]} {
+ # A compacted instruction is 8 bytes.
+ set next_pc [format 0x%x [expr $first_pc + 0x08]]
+ } else {
+ # A full instruction is 16 bytes.
+ set next_pc [format 0x%x [expr $first_pc + 0x10]]
+ }
+ set_breakpoint_bit $next_pc
+ }
+
+ # We should now step-over the current PC and hit the manually-inserted
+ # BP on the next PC.
+ gdb_test "continue" \
+ "Thread \[^\r\n\]+ received signal SIGTRAP.*" \
+ "continue to the manual BP"
+ set second_pc [get_hexadecimal_valueof "\$pc" 0 "second stop pc"]
+ gdb_assert {$second_pc == $next_pc}
+
+ # Resuming should not hit the manual BP again. Expect to terminate.
+ gdb_test "continue" "$inferior_exited_re normally].*" \
+ "continue to end"
+ }
+}
--
2.34.1
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread* [PATCH v3 44/44] testsuite, sycl: test canceling a stepping flow
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (42 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 43/44] testsuite, arch, intelgt: add intelgt-program-bp.exp Tankut Baris Aktemur
@ 2025-08-01 9:37 ` Tankut Baris Aktemur
2025-09-17 12:43 ` [PATCH v3 00/44] A new target to debug Intel GPUs Aktemur, Tankut Baris
` (2 subsequent siblings)
46 siblings, 0 replies; 92+ messages in thread
From: Tankut Baris Aktemur @ 2025-08-01 9:37 UTC (permalink / raw)
To: gdb-patches, Markus Metzger
If there exists a pending event for stepping in a range, this means
the stepping flow was broken because of another event, such as another
thread hitting a breakpoint. The stepping flow in this case is
canceled. Test it.
---
gdb/testsuite/gdb.sycl/step-canceled.exp | 86 ++++++++++++++++++++++++++++++++
1 file changed, 86 insertions(+)
diff --git a/gdb/testsuite/gdb.sycl/step-canceled.exp b/gdb/testsuite/gdb.sycl/step-canceled.exp
new file mode 100644
index 0000000000000000000000000000000000000000..5a4fae27d5ed9ce2c6050cc7622dbdc2d4d6b39a
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/step-canceled.exp
@@ -0,0 +1,86 @@
+# Copyright 2023-2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Test the target's handling of stepping.
+#
+# In this scenario we start stepping for a thread and resume the
+# others. There is a breakpoint close to the current PC. We expect
+# other threads to hit that BP before the current thread finishes
+# stepping its range. Hence, the step flow would be broken and
+# canceled. We should be able to resume the thread without problems.
+
+load_lib sycl.exp
+
+standard_testfile parallel-for-1D.cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+ unsupported "target does not support SYCL"
+ return 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+ "${binfile}" $srcfile {sycl debug}]} {
+ return -1
+}
+
+foreach device $sycl_device_list {
+ sycl_with_intelgt_lock $device {
+ clean_restart "${binfile}"
+
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ set first_line [gdb_get_line_number "kernel-first-line"]
+ set next_line [expr $first_line + 1]
+ gdb_breakpoint $first_line {temporary}
+
+ # Explicitly control the scheduler-locking setting.
+ gdb_test_no_output "set scheduler-locking off"
+
+ gdb_continue_to_breakpoint "hit the first line" \
+ ".*$srcfile:$first_line.*"
+ gdb_breakpoint $next_line
+
+ set first_thread [get_current_thread "first bp line"]
+
+ # Now only the second BP is there. The "step" command will
+ # start a stepping flow for the first thread whereas it will
+ # resume the others. We expect the others to hit the BP,
+ # because it is very close to our current PC, but of course
+ # there is a slight chance that this will not happen and the
+ # first thread will win the race.
+ gdb_test "step" "$srcfile:$next_line.*" "start stepping"
+ set second_thread [get_current_thread "second bp line"]
+
+ if {$first_thread eq $second_thread} {
+ # There was really a very very low chance of this happening.
+ untested "test condition could not be satisfied"
+ continue
+ }
+
+ # Resume the first thread only. It should hit the second BP
+ # normally.
+ gdb_test_no_output "set scheduler-locking on"
+ gdb_test "thread $first_thread" ".*" "switch to the first thread"
+ gdb_continue_to_breakpoint "resume the thread" \
+ ".*$srcfile:$next_line.*"
+
+ set curr_thread [get_current_thread "second bp line again"]
+ gdb_assert {"$curr_thread" eq "$first_thread"} \
+ "sanity-check the current thread"
+ }
+}
--
2.34.1
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread* RE: [PATCH v3 00/44] A new target to debug Intel GPUs
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (43 preceding siblings ...)
2025-08-01 9:37 ` [PATCH v3 44/44] testsuite, sycl: test canceling a stepping flow Tankut Baris Aktemur
@ 2025-09-17 12:43 ` Aktemur, Tankut Baris
2025-10-14 6:34 ` Aktemur, Tankut Baris
2025-12-08 11:32 ` Aktemur, Tankut Baris
2025-12-09 21:30 ` Simon Marchi
46 siblings, 1 reply; 92+ messages in thread
From: Aktemur, Tankut Baris @ 2025-09-17 12:43 UTC (permalink / raw)
To: gdb-patches; +Cc: Metzger, Markus T
On Friday, August 1, 2025 11:37 AM, Aktemur, Tankut Baris wrote:
> Hello,
>
> We (Intel) would like to submit patches to enable fundamental debug
> support for Intel GPU devices. In the future, we plan to add more
> patches that improve the performance and the user experience.
> Those patches are already available in the downstream "Intel
> Distribution for GDB" debugger at
>
> https://github.com/intel/gdb
>
> The v1 of the submission is available at
>
> https://sourceware.org/pipermail/gdb-patches/2024-July/210264.html
>
> and v2 is available at
>
> https://sourceware.org/pipermail/gdb-patches/2024-December/214029.html
>
> This revision (v3) makes the following changes:
>
> - The comments that have been received so far are addressed.
>
> - Patches are rebased on the master branch.
>
> - A number of patches have been refactored to improve the code.
Kindly pinging.
Review comments from maintainers would be greatly appreciated;
no comments from them have been received so far.
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] 92+ messages in thread* RE: [PATCH v3 00/44] A new target to debug Intel GPUs
2025-09-17 12:43 ` [PATCH v3 00/44] A new target to debug Intel GPUs Aktemur, Tankut Baris
@ 2025-10-14 6:34 ` Aktemur, Tankut Baris
0 siblings, 0 replies; 92+ messages in thread
From: Aktemur, Tankut Baris @ 2025-10-14 6:34 UTC (permalink / raw)
To: gdb-patches; +Cc: Metzger, Markus T
Kindly pinging.
-Baris
> > Hello,
> >
> > We (Intel) would like to submit patches to enable fundamental debug
> > support for Intel GPU devices. In the future, we plan to add more
> > patches that improve the performance and the user experience.
> > Those patches are already available in the downstream "Intel
> > Distribution for GDB" debugger at
> >
> > https://github.com/intel/gdb
> >
> > The v1 of the submission is available at
> >
> > https://sourceware.org/pipermail/gdb-patches/2024-July/210264.html
> >
> > and v2 is available at
> >
> > https://sourceware.org/pipermail/gdb-patches/2024-December/214029.html
> >
> > This revision (v3) makes the following changes:
> >
> > - The comments that have been received so far are addressed.
> >
> > - Patches are rebased on the master branch.
> >
> > - A number of patches have been refactored to improve the code.
>
> Kindly pinging.
>
> Review comments from maintainers would be greatly appreciated;
> no comments from them have been received so far.
>
> Regards,
> -Baris
Intel Deutschland GmbH
Registered Address: Dornacher Straße 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht München HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread
* RE: [PATCH v3 00/44] A new target to debug Intel GPUs
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (44 preceding siblings ...)
2025-09-17 12:43 ` [PATCH v3 00/44] A new target to debug Intel GPUs Aktemur, Tankut Baris
@ 2025-12-08 11:32 ` Aktemur, Tankut Baris
2025-12-09 21:30 ` Simon Marchi
46 siblings, 0 replies; 92+ messages in thread
From: Aktemur, Tankut Baris @ 2025-12-08 11:32 UTC (permalink / raw)
To: gdb-patches; +Cc: Metzger, Markus T
On Friday, August 1, 2025 11:37 AM, Aktemur, Tankut Baris wrote:
> Hello,
>
> We (Intel) would like to submit patches to enable fundamental debug
> support for Intel GPU devices. In the future, we plan to add more
> patches that improve the performance and the user experience.
> Those patches are already available in the downstream "Intel
> Distribution for GDB" debugger at
>
> https://github.com/intel/gdb
>
> The v1 of the submission is available at
>
> https://sourceware.org/pipermail/gdb-patches/2024-July/210264.html
>
> and v2 is available at
>
> https://sourceware.org/pipermail/gdb-patches/2024-December/214029.html
>
> This revision (v3) makes the following changes:
>
> - The comments that have been received so far are addressed.
>
> - Patches are rebased on the master branch.
>
> - A number of patches have been refactored to improve the code.
Kindly pinging.
Regards,
-Baris
Intel Deutschland GmbH
Registered Address: Dornacher Straße 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht München HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread* Re: [PATCH v3 00/44] A new target to debug Intel GPUs
2025-08-01 9:37 [PATCH v3 00/44] A new target to debug Intel GPUs Tankut Baris Aktemur
` (45 preceding siblings ...)
2025-12-08 11:32 ` Aktemur, Tankut Baris
@ 2025-12-09 21:30 ` Simon Marchi
2025-12-19 12:52 ` Aktemur, Tankut Baris
46 siblings, 1 reply; 92+ messages in thread
From: Simon Marchi @ 2025-12-09 21:30 UTC (permalink / raw)
To: Tankut Baris Aktemur, gdb-patches, Markus Metzger, binutils,
config-patches, Thiago Jung Bauermann
On 8/1/25 5:37 AM, Tankut Baris Aktemur wrote:
> Hello,
>
> We (Intel) would like to submit patches to enable fundamental debug
> support for Intel GPU devices. In the future, we plan to add more
> patches that improve the performance and the user experience.
> Those patches are already available in the downstream "Intel
> Distribution for GDB" debugger at
>
> https://github.com/intel/gdb
>
> The v1 of the submission is available at
>
> https://sourceware.org/pipermail/gdb-patches/2024-July/210264.html
>
> and v2 is available at
>
> https://sourceware.org/pipermail/gdb-patches/2024-December/214029.html
>
> This revision (v3) makes the following changes:
>
> - The comments that have been received so far are addressed.
>
> - Patches are rebased on the master branch.
>
> - A number of patches have been refactored to improve the code.
I suggest that you run a:
$ git rebase <base> -i -x 'pre-commit run --all-files'
It will catch some little nits for which checks were recently added.
Simon
^ permalink raw reply [flat|nested] 92+ messages in thread* RE: [PATCH v3 00/44] A new target to debug Intel GPUs
2025-12-09 21:30 ` Simon Marchi
@ 2025-12-19 12:52 ` Aktemur, Tankut Baris
0 siblings, 0 replies; 92+ messages in thread
From: Aktemur, Tankut Baris @ 2025-12-19 12:52 UTC (permalink / raw)
To: Simon Marchi, gdb-patches
Cc: binutils, Thiago Jung Bauermann, Metzger, Markus T
Hi Simon,
Thank you very much for taking the time to review the series.
On Tuesday, December 9, 2025 10:31 PM, Simon Marchi wrote:
> On 8/1/25 5:37 AM, Tankut Baris Aktemur wrote:
> > Hello,
> >
> > We (Intel) would like to submit patches to enable fundamental debug
> > support for Intel GPU devices. In the future, we plan to add more
> > patches that improve the performance and the user experience.
> > Those patches are already available in the downstream "Intel
> > Distribution for GDB" debugger at
> >
> > https://github.com/intel/gdb
> >
> > The v1 of the submission is available at
> >
> > https://sourceware.org/pipermail/gdb-patches/2024-July/210264.html
> >
> > and v2 is available at
> >
> > https://sourceware.org/pipermail/gdb-patches/2024-
> December/214029.html
> >
> > This revision (v3) makes the following changes:
> >
> > - The comments that have been received so far are addressed.
> >
> > - Patches are rebased on the master branch.
> >
> > - A number of patches have been refactored to improve the code.
>
> I suggest that you run a:
>
> $ git rebase <base> -i -x 'pre-commit run --all-files'
>
> It will catch some little nits for which checks were recently added.
>
> Simon
We will do this before submitting the next revision.
Regards,
-Baris
Intel Deutschland GmbH
Registered Address: Dornacher Straße 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht München HRB 186928
^ permalink raw reply [flat|nested] 92+ messages in thread