[PATCH v2 07/47] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description

Tankut Baris Aktemur
2024-12-13T16:19:06Z

From: Nils-Christian Kempke <nils-christian.kempke@intel.com>

Add <device> tag to the XML target description. The tag is a list of
device attributes. The extension enables passing device information
within the target description XML sent from gdbserver to gdb.

Co-authored-by: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
gdb/NEWS | 8 ++++
gdb/doc/gdb.texinfo | 25 +++++++++++++
gdb/features/gdb-target.dtd | 19 +++++++++-
gdb/target-descriptions.c | 19 ++++++++++
gdb/xml-tdesc.c | 76 ++++++++++++++++++++++++++++++++++++++
gdbserver/tdesc.cc | 16 ++++++++
gdbserver/tdesc.h | 3 ++
gdbsupport/tdesc.cc | 40 ++++++++++++++++++++
gdbsupport/tdesc.h | 90 +++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 295 insertions(+), 1 deletion(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index 361d7726ba0f7065b1ad78c34aa0decb860f4e9c..8f8aa1462f5f757bda3d25ca60e2eba80660bcb6 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -174,6 +174,14 @@ vFile:stat
vFile:fstat but takes a filename rather than an open file
descriptor.

+* Changed remote packets
+
+qXfer:features:read:target.xml
+
+ The XML that is sent as a response can now include a "device" element
+ that can be used for passing information when the remote inferior
+ represents a device, e.g. a GPU.
+
*** Changes in GDB 15

* The MPX commands "show/set mpx bound" have been deprecated, as Intel
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 85ac3d9aab63d790b66aae8bfe38156b44f4e8fc..ddd414659fcc554813fc2a12a79e581ad7a188b9 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -48552,6 +48552,7 @@ are explained further below.
@r{[}@var{architecture}@r{]}
@r{[}@var{osabi}@r{]}
@r{[}@var{compatible}@r{]}
+ @r{[}@var{device}@r{]}
@r{[}@var{feature}@dots{}@r{]}
</target>
@end smallexample
@@ -48649,6 +48650,30 @@ capability with @samp{<compatible>} is as follows:
<compatible>spu</compatible>
@end smallexample

+@subsection Device Information
+@cindex <device>
+
+A @samp{<device>} element can be used for passing device information
+for when, e.g.@: the remote target represents a device, such as a GPU.
+@value{GDBN} can then use this information to display it to the user
+for informative purposes or to execute device-specific functions
+appropriately. The element contains a number of attributes, best
+shown with the example below. All attributes are optional.
+
+@smallexample
+ <device vendor-id="0x8086"
+ target-id="0x56a1"
+ family="abc"
+ model="xyz"
+ stepping="3"
+ name="Intel(R) Arc(TM) A750 Graphics"
+ pci-slot="03:00.0"
+ uuid="0003000856a18086"
+ total-cores="448"
+ total-threads="3584"
+ subdevice-id="1"/>
+@end smallexample
+
@subsection Features
@cindex <feature>

diff --git a/gdb/features/gdb-target.dtd b/gdb/features/gdb-target.dtd
index d07703fca8b6b22bffb8acea99b40ce7f7a590c8..4afaffc6d0c228ee14f2e46492b152245dea3990 100644
--- a/gdb/features/gdb-target.dtd
+++ b/gdb/features/gdb-target.dtd
@@ -9,7 +9,9 @@
<!-- The osabi and compatible elements were added post GDB 6.8. The version
wasn't bumped, since older GDBs silently ignore unknown elements. -->

-<!ELEMENT target (architecture?, osabi?, compatible*, feature*)>
+<!ELEMENT target
+ (architecture?, osabi?, compatible*, device?, feature*)>
+
<!ATTLIST target
version CDATA #FIXED "1.0">

@@ -19,6 +21,21 @@

<!ELEMENT compatible (#PCDATA)>

+<!ELEMENT device EMPTY>
+<!ATTLIST device
+ vendor-id CDATA #IMPLIED
+ target-id CDATA #IMPLIED
+ family CDATA #IMPLIED
+ model CDATA #IMPLIED
+ stepping CDATA #IMPLIED
+ name CDATA #IMPLIED
+ pci-slot CDATA #IMPLIED
+ uuid CDATA #IMPLIED
+ total-cores CDATA #IMPLIED
+ total-threads CDATA #IMPLIED
+ subdevice-id CDATA #IMPLIED
+ >
+
<!ELEMENT feature
((vector | flags | struct | union )*, reg*)>
<!ATTLIST feature
diff --git a/gdb/target-descriptions.c b/gdb/target-descriptions.c
index 3ee8a751f3b303b7d02df40b3b3836c8a8c83066..34a00265b4bb601995baffcd0d99ce26af4264cc 100644
--- a/gdb/target-descriptions.c
+++ b/gdb/target-descriptions.c
@@ -358,6 +358,9 @@ struct target_desc : tdesc_element
/* The list of compatible architectures reported by the target. */
std::vector<tdesc_compatible_info_up> compatible;

+ /* The device reported by the target, if any. */
+ tdesc_device_up device;
+
/* Any architecture-specific properties specified by the target. */
std::vector<property> properties;

@@ -643,6 +646,22 @@ tdesc_osabi_name (const struct target_desc *target_desc)
return nullptr;
}

+/* See gdbsupport/tdesc.h. */
+
+void
+set_tdesc_device_info (target_desc *target_desc, tdesc_device *device)
+{
+ target_desc->device.reset (device);
+}
+
+/* See gdbsupport/tdesc.h. */
+
+const tdesc_device *
+tdesc_device_info (const target_desc *target_desc)
+{
+ return target_desc->device.get ();
+}
+
/* Return 1 if this target description includes any registers. */

int
diff --git a/gdb/xml-tdesc.c b/gdb/xml-tdesc.c
index 436c493d4f914eb71a80bcbc08dc2ddd38c72027..61cba42e9eee2ec4cc66e14a120c5bef909e48b0 100644
--- a/gdb/xml-tdesc.c
+++ b/gdb/xml-tdesc.c
@@ -137,6 +137,65 @@ tdesc_end_compatible (struct gdb_xml_parser *parser,
tdesc_add_compatible (data->tdesc, arch);
}

+/* Handle the end of a <device> element and its value. */
+
+static void
+tdesc_start_device (struct gdb_xml_parser *parser,
+ const gdb_xml_element *element,
+ void *user_data, std::vector<gdb_xml_value> &attributes)
+{
+ tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+ tdesc_device *device = new tdesc_device ();
+
+ gdb_xml_value *attr;
+
+ attr = xml_find_attribute (attributes, "vendor-id");
+ if (attr != nullptr)
+ device->vendor_id = * (ULONGEST *) attr->value.get ();
+
+ attr = xml_find_attribute (attributes, "target-id");
+ if (attr != nullptr)
+ device->target_id = * (ULONGEST *) attr->value.get ();
+
+ attr = xml_find_attribute (attributes, "family");
+ if (attr != nullptr)
+ device->family = (char *) attr->value.get ();
+
+ attr = xml_find_attribute (attributes, "model");
+ if (attr != nullptr)
+ device->model = (char *) attr->value.get ();
+
+ attr = xml_find_attribute (attributes, "stepping");
+ if (attr != nullptr)
+ device->stepping = * (ULONGEST *) attr->value.get ();
+
+ attr = xml_find_attribute (attributes, "name");
+ if (attr != nullptr)
+ device->name = (char *) attr->value.get ();
+
+ attr = xml_find_attribute (attributes, "pci-slot");
+ if (attr != nullptr)
+ device->pci_slot = (char *) attr->value.get ();
+
+ attr = xml_find_attribute (attributes, "uuid");
+ if (attr != nullptr)
+ device->uuid = (char *) attr->value.get ();
+
+ attr = xml_find_attribute (attributes, "total-cores");
+ if (attr != nullptr)
+ device->total_cores = * (ULONGEST *) attr->value.get ();
+
+ attr = xml_find_attribute (attributes, "total-threads");
+ if (attr != nullptr)
+ device->total_threads = * (ULONGEST *) attr->value.get ();
+
+ attr = xml_find_attribute (attributes, "subdevice-id");
+ if (attr != nullptr)
+ device->subdevice_id = * (ULONGEST *) attr->value.get ();
+
+ set_tdesc_device_info (data->tdesc, device);
+}
+
/* Handle the start of a <target> element. */

static void
@@ -588,6 +647,21 @@ static const struct gdb_xml_element feature_children[] = {
{ NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
};

+static const struct gdb_xml_attribute device_attributes[] = {
+ { "vendor-id", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+ { "target-id", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+ { "family", GDB_XML_AF_OPTIONAL, NULL, NULL },
+ { "model", GDB_XML_AF_OPTIONAL, NULL, NULL },
+ { "stepping", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+ { "name", GDB_XML_AF_OPTIONAL, NULL, NULL },
+ { "pci-slot", GDB_XML_AF_OPTIONAL, NULL, NULL },
+ { "uuid", GDB_XML_AF_OPTIONAL, NULL, NULL },
+ { "total-cores", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+ { "total-threads", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+ { "subdevice-id", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL},
+ { NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
static const struct gdb_xml_attribute target_attributes[] = {
{ "version", GDB_XML_AF_NONE, NULL, NULL },
{ NULL, GDB_XML_AF_NONE, NULL, NULL }
@@ -600,6 +674,8 @@ static const struct gdb_xml_element target_children[] = {
NULL, tdesc_end_osabi },
{ "compatible", NULL, NULL, GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE,
NULL, tdesc_end_compatible },
+ { "device", device_attributes, NULL, GDB_XML_EF_OPTIONAL,
+ tdesc_start_device, NULL },
{ "feature", feature_attributes, feature_children,
GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE,
tdesc_start_feature, NULL },
diff --git a/gdbserver/tdesc.cc b/gdbserver/tdesc.cc
index da1287abbbed5933bf8ceddd4aa5d7a70ad4d473..dc89ac1cd4f3a189b2e3ba30998119e8e4df4531 100644
--- a/gdbserver/tdesc.cc
+++ b/gdbserver/tdesc.cc
@@ -190,6 +190,22 @@ set_tdesc_osabi (struct target_desc *target_desc, enum gdb_osabi osabi)

/* See gdbsupport/tdesc.h. */

+void
+set_tdesc_device_info (target_desc *target_desc, tdesc_device *device)
+{
+ target_desc->device.reset (device);
+}
+
+/* See gdbsupport/tdesc.h. */
+
+const tdesc_device *
+tdesc_device_info (const target_desc *target_desc)
+{
+ return target_desc->device.get ();
+}
+
+/* See gdbsupport/tdesc.h. */
+
const char *
tdesc_get_features_xml (const target_desc *tdesc)
{
diff --git a/gdbserver/tdesc.h b/gdbserver/tdesc.h
index 9264786de51173c262aadcaa31d205f0b274b7c4..3eb6d67db02f7f36b197d499f456d9cabeffa833 100644
--- a/gdbserver/tdesc.h
+++ b/gdbserver/tdesc.h
@@ -60,6 +60,9 @@ struct target_desc final : tdesc_element
/* The value of <osabi> element in the XML, replying GDB. */
gdb::unique_xmalloc_ptr<char> osabi;

+ /* The value of the <device> element in the XML, replying GDB. */
+ tdesc_device_up device;
+
public:
target_desc ()
: registers_size (0)
diff --git a/gdbsupport/tdesc.cc b/gdbsupport/tdesc.cc
index 080d39c485dc5d9d7ced21e067bec5391b132c94..ae7a7b36c7ca2d265c5707437239801599e7fb62 100644
--- a/gdbsupport/tdesc.cc
+++ b/gdbsupport/tdesc.cc
@@ -417,6 +417,8 @@ void print_xml_feature::visit_pre (const target_desc *e)
for (const auto &c : compatible_list)
add_line ("<compatible>%s</compatible>",
tdesc_compatible_info_arch_name (c));
+
+ this->visit (tdesc_device_info (e));
#endif
}

@@ -426,6 +428,44 @@ void print_xml_feature::visit_post (const target_desc *e)
add_line ("</target>");
}

+void
+print_xml_feature::visit (const tdesc_device *device)
+{
+ if (device == nullptr)
+ return;
+
+ std::string tmp = "<device";
+ if (device->vendor_id.has_value ())
+ string_appendf (tmp, " vendor-id=\"0x%04" PRIx32 "\"",
+ *device->vendor_id);
+
+ if (device->target_id.has_value ())
+ string_appendf (tmp, " target-id=\"0x%04" PRIx32 "\"",
+ *device->target_id);
+
+ string_appendf (tmp, " family=\"%s\"", device->family.c_str ());
+ string_appendf (tmp, " model=\"%s\"", device->model.c_str ());
+
+ if (device->stepping.has_value ())
+ string_appendf (tmp, " stepping=\"%d\"", *device->stepping);
+
+ string_appendf (tmp, " name=\"%s\"", device->name.c_str ());
+ string_appendf (tmp, " pci-slot=\"%s\"", device->pci_slot.c_str ());
+ string_appendf (tmp, " uuid=\"%s\"", device->uuid.c_str ());
+
+ if (device->total_cores.has_value ())
+ string_appendf (tmp, " total-cores=\"%ld\"", *device->total_cores);
+
+ if (device->total_threads.has_value ())
+ string_appendf (tmp, " total-threads=\"%ld\"", *device->total_threads);
+
+ if (device->subdevice_id.has_value ())
+ string_appendf (tmp, " subdevice-id=\"%d\"", *device->subdevice_id);
+
+ string_appendf (tmp, "/>");
+ add_line (tmp);
+}
+
/* See gdbsupport/tdesc.h. */

void
diff --git a/gdbsupport/tdesc.h b/gdbsupport/tdesc.h
index c9e7603369cbf986187908c2d1f8fbe35a0dc02d..29de4d231f16667c8297bcbe403f683c30a28762 100644
--- a/gdbsupport/tdesc.h
+++ b/gdbsupport/tdesc.h
@@ -26,6 +26,7 @@ struct tdesc_type_builtin;
struct tdesc_type_vector;
struct tdesc_type_with_fields;
struct tdesc_reg;
+struct tdesc_device;
struct target_desc;

/* The interface to visit different elements of target description. */
@@ -56,6 +57,9 @@ class tdesc_element_visitor

virtual void visit (const tdesc_reg *e)
{}
+
+ virtual void visit (const tdesc_device *e)
+ {}
};

class tdesc_element
@@ -315,6 +319,84 @@ struct tdesc_feature : tdesc_element

typedef std::unique_ptr<tdesc_feature> tdesc_feature_up;

+/* The device information in a target description. */
+
+struct tdesc_device : tdesc_element
+{
+ tdesc_device ()
+ {
+ family = "-";
+ model = "-";
+ name = "-";
+ pci_slot = "-";
+ uuid = "-";
+ }
+
+ virtual ~tdesc_device () = default;
+
+ DISABLE_COPY_AND_ASSIGN (tdesc_device);
+
+ /* The id of the device vendor. */
+ std::optional<uint32_t> vendor_id;
+
+ /* The id of the device, given by its vendor. */
+ std::optional<uint32_t> target_id;
+
+ /* The family of the device. */
+ std::string family;
+
+ /* The model of the device. */
+ std::string model;
+
+ /* The stepping of the device. */
+ std::optional<uint32_t> stepping;
+
+ /* The name of the device. */
+ std::string name;
+
+ /* The location where the device is positioned. */
+ std::string pci_slot;
+
+ /* The UUID of the device. */
+ std::string uuid;
+
+ /* The number of cores available in the device. */
+ std::optional<size_t> total_cores;
+
+ /* The number of threads available in the device. */
+ std::optional<size_t> total_threads;
+
+ /* The subdevice id, if this device is a subdevice. */
+ std::optional<uint32_t> subdevice_id;
+
+ void accept (tdesc_element_visitor &v) const override
+ {
+ v.visit (this);
+ }
+
+ bool operator== (const tdesc_device &other) const
+ {
+ return (vendor_id == other.vendor_id
+ && target_id == other.target_id
+ && family == other.family
+ && model == other.model
+ && stepping == other.stepping
+ && name == other.name
+ && pci_slot == other.pci_slot
+ && uuid == other.uuid
+ && total_cores == other.total_cores
+ && total_threads == other.total_threads
+ && subdevice_id == other.subdevice_id);
+ }
+
+ bool operator!= (const tdesc_device &other) const
+ {
+ return !(*this == other);
+ }
+};
+
+typedef std::unique_ptr<tdesc_device> tdesc_device_up;
+
/* A deleter adapter for a target_desc. There are different
implementations of this deleter class in gdb and gdbserver because even
though the target_desc name is shared between the two projects, the
@@ -347,6 +429,13 @@ void set_tdesc_osabi (target_desc *target_desc, enum gdb_osabi osabi);
or NULL if no osabi was specified. */
const char *tdesc_osabi_name (const struct target_desc *target_desc);

+/* Set TARGET_DESC's device information. */
+void set_tdesc_device_info (target_desc *target_desc, tdesc_device *device);
+
+/* Return the device information associated with this target
+ description or NULL if device info does not exist. */
+const tdesc_device *tdesc_device_info (const target_desc *target_desc);
+
/* Return the type associated with ID in the context of FEATURE, or
NULL if none. */
struct tdesc_type *tdesc_named_type (const struct tdesc_feature *feature,
@@ -439,6 +528,7 @@ class print_xml_feature : public tdesc_element_visitor
void visit (const tdesc_type_vector *type) override;
void visit (const tdesc_type_with_fields *type) override;
void visit (const tdesc_reg *reg) override;
+ void visit (const tdesc_device *device) override;