+2020-06-23 Andrew Burgess <andrew.burgess@embecosm.com>
+
+ * target-descriptions.c (tdesc_architecture_name): Protect against
+ NULL pointer dereference.
+ (maint_print_xml_tdesc_cmd): New function.
+ (_initialize_target_descriptions): Register new 'maint print
+ xml-tdesc' command and give it the filename completer.
+ * NEWS: Mention new 'maint print xml-tdesc' command.
+
2020-06-23 Andrew Burgess <andrew.burgess@embecosm.com>
* target-descriptions.c (class tdesc_compatible_info): New class.
Define a new TUI layout, specifying its name and the windows that
will be displayed.
+maintenance print xml-tdesc [FILE]
+ Prints the current target description as an XML document. If the
+ optional FILE is provided (which is an XML target description) then
+ the target description is read from FILE into GDB, and then
+ reprinted.
+
* Changed commands
alias [-a] [--] ALIAS = COMMAND [DEFAULT-ARGS...]
+2020-06-23 Andrew Burgess <andrew.burgess@embecosm.com>
+
+ * gdb.texinfo (Maintenance Commands): Document new 'maint print
+ xml-desc' command.
+
2020-06-22 Philippe Waroquiers <philippe.waroquiers@skynet.be>
* gdb.texinfo (Command aliases default args): New node documenting
built again. This command is used by developers after they add or
modify XML target descriptions.
+@kindex maint print xml-tdesc
+@item maint print xml-tdesc @r{[}@var{file}@r{]}
+Print the target description (@pxref{Target Descriptions}) as an XML
+file. By default print the target description for the current target,
+but if the optional argument @var{file} is provided, then that file is
+read in by GDB and then used to produce the description. The
+@var{file} should be an XML document, of the form described in
+@ref{Target Description Format}.
+
@kindex maint check xml-descriptions
@item maint check xml-descriptions @var{dir}
Check that the target descriptions dynamically created by @value{GDBN}
const char *
tdesc_architecture_name (const struct target_desc *target_desc)
{
- return target_desc->arch->printable_name;
+ if (target_desc->arch != NULL)
+ return target_desc->arch->printable_name;
+ return NULL;
}
/* See gdbsupport/tdesc.h. */
}
}
+/* Implement the maintenance print xml-tdesc command. */
+
+static void
+maint_print_xml_tdesc_cmd (const char *args, int from_tty)
+{
+ const struct target_desc *tdesc;
+
+ if (args == NULL)
+ {
+ /* Use the global target-supplied description, not the current
+ architecture's. This lets a GDB for one architecture generate XML
+ for another architecture's description, even though the gdbarch
+ initialization code will reject the new description. */
+ tdesc = current_target_desc;
+ }
+ else
+ {
+ /* Use the target description from the XML file. */
+ tdesc = file_read_description_xml (args);
+ }
+
+ if (tdesc == NULL)
+ error (_("There is no target description to print."));
+
+ std::string buf;
+ print_xml_feature v (&buf);
+ tdesc->accept (v);
+ puts_unfiltered (buf.c_str ());
+}
+
namespace selftests {
/* A reference target description, used for testing (see record_xml_tdesc). */
&maintenanceprintlist);
set_cmd_completer (cmd, filename_completer);
+ cmd = add_cmd ("xml-tdesc", class_maintenance, maint_print_xml_tdesc_cmd, _("\
+Print the current target description as an XML file."),
+ &maintenanceprintlist);
+ set_cmd_completer (cmd, filename_completer);
+
cmd = add_cmd ("xml-descriptions", class_maintenance,
maintenance_check_xml_descriptions, _("\
Check equality of GDB target descriptions and XML created descriptions.\n\
+2020-06-23 Andrew Burgess <andrew.burgess@embecosm.com>
+
+ * gdb.xml/tdesc-reload.c: New file.
+ * gdb.xml/tdesc-reload.exp: New file.
+ * gdb.xml/maint-xml-dump-01.xml: New file.
+ * gdb.xml/maint-xml-dump-02.xml: New file.
+ * gdb.xml/maint-xml-dump.exp: New file.
+
2020-06-23 Sandra Loosemore <sandra@codesourcery.com>
* lib/completion-support.exp (test_gdb_completion_offers_commands):
--- /dev/null
+<?xml version="1.0"?>
+<!-- This is a comment before DOCTYPE -->
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<!-- This is a comment after DOCTYPE -->
+<target>
+ <feature name="abc">
+ <!-- The following is a register. -->
+ <reg name="r1" bitsize="32"/> <!-- <reg name="r1" bitsize="32" type="int" regnum="0"/> -->
+ </feature>
+</target>
--- /dev/null
+<target>
+ <osabi>Solaris</osabi>
+ <feature name="abc">
+ <vector id="foo" type="int32" count="4"/>
+ <reg name="foo" bitsize="16" /> <!-- <reg name="foo" bitsize="16" type="int" regnum="0"/> -->
+ </feature>
+ <feature name="def.xyz">
+ <struct id="my_struct">
+ <field name="field1" type="int8"/>
+ <field name="field2" type="int16"/>
+ <field name="field3" type="int8"/>
+ </struct>
+ <struct id="bit_field" size="8">
+ <field name="bits1" start="0" end="3" type="int8"/>
+ <field name="bits2" start="4" end="6" type="int8"/>
+ <field name="bits3" start="7" end="7"/> <!-- <field name="bits3" start="7" end="7" type="bool"/> -->
+ </struct>
+ <flags id="my_flags" size="8">
+ <field name="flg1" start="0" end="0"/> <!-- <field name="flg1" start="0" end="0" type="bool"/> -->
+ <field name="flg2" start="1" end="1"/> <!-- <field name="flg2" start="1" end="1" type="bool"/> -->
+ <field name="flg3" start="2" end="6"/> <!-- <field name="flg3" start="2" end="6" type="uint64"/> -->
+ <field name="flg4" start="7" end="7"/> <!-- <field name="flg4" start="7" end="7" type="bool"/> -->
+ </flags>
+ <reg name="r1" bitsize="8" type="my_flags"/> <!-- <reg name="r1" bitsize="8" type="my_flags" regnum="1"/> -->
+ <reg name="r2" bitsize="8" type="bit_field"/> <!-- <reg name="r2" bitsize="8" type="bit_field" regnum="2"/> -->
+ </feature>
+</target>
--- /dev/null
+# Copyright 2020 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 'maint print xml-tdesc' command. This file picks up every
+# XML file matching the pattern maint-xml-dump-*.xml (in the same
+# directory as this script) and passes each in turn to the command
+# 'maint print xml-tdesc'.
+#
+# The expected output is generated by parsing the input XML file. The
+# rules for changing an XML file into the expected output are:
+#
+# 1. Blank lines, and lines starting with a comment are stripped from
+# the expected output.
+#
+# 2. The <?xml ... ?> and <!DOCTYPE ...> entities are optional,
+# suitable defaults will be added if these lines are missing from
+# the input file.
+#
+# 3. A trailing comment on a line will replace the expected output for
+# that line but with the indentation of the line preserved. So
+# this (The '|' marks the start of the line):
+# | <reg name="r1" bitsize="32"/> <!-- <reg name="r1" bitsize="32" type="int" regnum="0"/> -->
+# Will actually look for the following output:
+# | <reg name="r1" bitsize="32" type="int" regnum="0"/>
+#
+# 4. Indentation of lines will be preserved so your input file needs
+# to follow the expected indentation.
+if {[gdb_skip_xml_test]} {
+ unsupported "xml tests not being run"
+ return -1
+}
+
+gdb_start
+
+# Read the XML file FILENAME and produce an output pattern that should
+# match what GDB produces with the 'maint print xml-desc' command.
+proc build_pattern { filename } {
+ set pattern {}
+
+ set xml_version_line {<?xml version="1.0"?>}
+ set doc_type_line {<!DOCTYPE target SYSTEM "gdb-target.dtd">}
+
+ set linenum 0
+ set ifd [open "$filename" r]
+ while {[gets $ifd line] >= 0} {
+ incr linenum
+
+ # The <?xml .... ?> tag can only appear as the first line in
+ # the file. If it is not present then add one to the expected
+ # output now.
+ if {$linenum == 1} {
+ if {![regexp {^<\?xml} $line]} {
+ set pattern [string_to_regexp $xml_version_line]
+ set xml_version_line ""
+ }
+ }
+
+ # If we have not yet seen a DOCTYPE line, then maybe we should
+ # be adding one? If we find <target> then add a default
+ # DOCTYPE line, otherwise, if the XML file includes a DOCTYPE
+ # line, use that.
+ if {$doc_type_line != "" } {
+ if {[regexp {^[ \t]*<target>} $line]} {
+ set pattern [multi_line $pattern \
+ [string_to_regexp $doc_type_line]]
+ set doc_type_line ""
+ } elseif {[regexp {^[ \t]*<!DOCTYPE } $line]} {
+ set doc_type_line ""
+ }
+ }
+
+ if {[regexp {^[ \t]*<!--} $line]} {
+ # Comment line, ignore it.
+ } elseif {[regexp {^[ \t]+$} $line]} {
+ # Blank line, ignore it.
+ } elseif {[regexp {^([ \t]*).*<!-- (.*) -->$} $line \
+ matches grp1 grp2]} {
+ set pattern [multi_line \
+ $pattern \
+ [string_to_regexp "$grp1$grp2"]]
+ } else {
+ set pattern [multi_line \
+ $pattern \
+ [string_to_regexp $line]]
+ }
+ }
+ close $ifd
+
+ # Due to handling the <?xml ...?> tags we can end up with a stray
+ # '\r\n' at the start of the output pattern. Remove it here.
+ if {[string range $pattern 0 1] == "\r\n"} {
+ set pattern [string range $pattern 2 end]
+ }
+
+ return $pattern
+}
+
+# Run over every test XML file and check the output.
+foreach filename [lsort [glob $srcdir/$subdir/maint-xml-dump-*.xml]] {
+ set pattern [build_pattern $filename]
+
+ if {[is_remote host]} {
+ set test_path [remote_download host $filename]
+ } else {
+ set test_path $filename
+ }
+
+ verbose -log "Looking for:\n$pattern"
+
+ gdb_test "maint print xml-tdesc $test_path" \
+ "$pattern" "check [file tail $filename]"
+}
--- /dev/null
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2020 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/>. */
+
+int
+main ()
+{
+ return 0;
+}
--- /dev/null
+# Copyright 2020 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/>.
+
+# Testing for 'maint print xml-tdesc'. Check we can print out the
+# current target description and load it back in again.
+
+if {[gdb_skip_xml_test]} {
+ unsupported "xml tests not being run"
+ return -1
+}
+
+standard_testfile
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile debug]} {
+ return -1
+}
+
+if ![runto_main] then {
+ fail "can't run to main"
+ return 0
+}
+
+# Three files we're going to write out to.
+set xml_file_1 [standard_output_file outfile1.xml]
+set xml_file_2 [standard_output_file outfile2.xml]
+set xml_file_3 [standard_output_file outfile3.xml]
+
+# Write the current target description to a file.
+gdb_test_no_output "pipe maint print xml-tdesc | cat > $xml_file_1" \
+ "write current target description to file"
+
+# Read the target description back in to GDB, and the write it back
+# out to a file.
+gdb_test_no_output \
+ "pipe maint print xml-tdesc $xml_file_1 | cat > $xml_file_2" \
+ "read previous xml description, and write it out to a second file"
+
+# Check the two produced files are identical.
+gdb_test "shell diff -s $xml_file_1 $xml_file_2" \
+ "Files \[^\r\n\]* are identical" \
+ "first two produced xml files are identical"
+
+# Restart GDB.
+clean_restart
+
+# Change to use one of the target descriptions we wrote out earlier.
+gdb_test_no_output "set tdesc filename $xml_file_1" \
+ "set target description to use"
+
+# Load the executable.
+gdb_load ${binfile}
+
+# Run to `main' where we begin our tests.
+if ![runto_main] then {
+ untested "could not run to main"
+ return -1
+}
+
+# Run info registers just to check this appears to run fine with the
+# new target description.
+gdb_test "info all-registers" ".*" \
+ "Run info registers"
+
+# Write out the current target description.
+gdb_test_no_output "pipe maint print xml-tdesc | cat > $xml_file_3" \
+ "write third target description to file"
+
+# And check that it matches the original file we loaded.
+gdb_test "shell diff -s $xml_file_1 $xml_file_3" \
+ "Files \[^\r\n\]* are identical" \
+ "first and third produced xml files are identical"
+2020-06-23 Andrew Burgess <andrew.burgess@embecosm.com>
+
+ * tdesc.cc (print_xml_feature::visit_pre): Use add_line to add
+ output content, and call indent as needed in all overloaded
+ variants.
+ (print_xml_feature::visit_post): Likewise.
+ (print_xml_feature::visit): Likewise.
+ (print_xml_feature::add_line): Two new overloaded functions.
+ * tdesc.h (print_xml_feature::indent): New member function.
+ (print_xml_feature::add_line): Two new overloaded member
+ functions.
+ (print_xml_feature::m_depth): New member variable.
+
2020-06-23 Andrew Burgess <andrew.burgess@embecosm.com>
* tdesc.cc (print_xml_feature::visit_pre): Print compatible
void print_xml_feature::visit_pre (const tdesc_feature *e)
{
- string_appendf (*m_buffer, "<feature name=\"%s\">\n", e->name.c_str ());
+ add_line ("<feature name=\"%s\">", e->name.c_str ());
+ indent (1);
}
void print_xml_feature::visit_post (const tdesc_feature *e)
{
- string_appendf (*m_buffer, "</feature>\n");
+ indent (-1);
+ add_line ("</feature>");
}
void print_xml_feature::visit (const tdesc_type_builtin *t)
void print_xml_feature::visit (const tdesc_type_vector *t)
{
- string_appendf (*m_buffer, "<vector id=\"%s\" type=\"%s\" count=\"%d\"/>\n",
- t->name.c_str (), t->element_type->name.c_str (), t->count);
+ add_line ("<vector id=\"%s\" type=\"%s\" count=\"%d\"/>",
+ t->name.c_str (), t->element_type->name.c_str (), t->count);
}
void print_xml_feature::visit (const tdesc_type_with_fields *t)
gdb_assert (t->kind >= TDESC_TYPE_STRUCT && t->kind <= TDESC_TYPE_ENUM);
- string_appendf (*m_buffer,
+ std::string tmp;
+
+ string_appendf (tmp,
"<%s id=\"%s\"", types[t->kind - TDESC_TYPE_STRUCT],
t->name.c_str ());
case TDESC_TYPE_STRUCT:
case TDESC_TYPE_FLAGS:
if (t->size > 0)
- string_appendf (*m_buffer, " size=\"%d\"", t->size);
- string_appendf (*m_buffer, ">\n");
+ string_appendf (tmp, " size=\"%d\"", t->size);
+ string_appendf (tmp, ">");
+ add_line (tmp);
for (const tdesc_type_field &f : t->fields)
{
- string_appendf (*m_buffer, " <field name=\"%s\" ", f.name.c_str ());
- if (f.start == -1)
- string_appendf (*m_buffer, "type=\"%s\"/>\n",
- f.type->name.c_str ());
- else
- string_appendf (*m_buffer, "start=\"%d\" end=\"%d\"/>\n", f.start,
+ tmp.clear ();
+ string_appendf (tmp, " <field name=\"%s\"", f.name.c_str ());
+ if (f.start != -1)
+ string_appendf (tmp, " start=\"%d\" end=\"%d\"", f.start,
f.end);
+ string_appendf (tmp, " type=\"%s\"/>",
+ f.type->name.c_str ());
+ add_line (tmp);
}
break;
case TDESC_TYPE_ENUM:
- string_appendf (*m_buffer, ">\n");
+ string_appendf (tmp, ">");
+ add_line (tmp);
for (const tdesc_type_field &f : t->fields)
- string_appendf (*m_buffer, " <field name=\"%s\" start=\"%d\"/>\n",
- f.name.c_str (), f.start);
+ add_line (" <field name=\"%s\" start=\"%d\"/>",
+ f.name.c_str (), f.start);
break;
case TDESC_TYPE_UNION:
- string_appendf (*m_buffer, ">\n");
+ string_appendf (tmp, ">");
+ add_line (tmp);
for (const tdesc_type_field &f : t->fields)
- string_appendf (*m_buffer, " <field name=\"%s\" type=\"%s\"/>\n",
- f.name.c_str (), f.type->name.c_str ());
+ add_line (" <field name=\"%s\" type=\"%s\"/>",
+ f.name.c_str (), f.type->name.c_str ());
break;
default:
t->name.c_str ());
}
- string_appendf (*m_buffer, "</%s>\n", types[t->kind - TDESC_TYPE_STRUCT]);
+ add_line ("</%s>", types[t->kind - TDESC_TYPE_STRUCT]);
}
void print_xml_feature::visit (const tdesc_reg *r)
{
- string_appendf (*m_buffer,
+ std::string tmp;
+
+ string_appendf (tmp,
"<reg name=\"%s\" bitsize=\"%d\" type=\"%s\" regnum=\"%ld\"",
r->name.c_str (), r->bitsize, r->type.c_str (),
r->target_regnum);
if (r->group.length () > 0)
- string_appendf (*m_buffer, " group=\"%s\"", r->group.c_str ());
+ string_appendf (tmp, " group=\"%s\"", r->group.c_str ());
if (r->save_restore == 0)
- string_appendf (*m_buffer, " save-restore=\"no\"");
+ string_appendf (tmp, " save-restore=\"no\"");
- string_appendf (*m_buffer, "/>\n");
+ string_appendf (tmp, "/>");
+
+ add_line (tmp);
}
void print_xml_feature::visit_pre (const target_desc *e)
{
#ifndef IN_PROCESS_AGENT
- string_appendf (*m_buffer, "<?xml version=\"1.0\"?>\n");
- string_appendf (*m_buffer, "<!DOCTYPE target SYSTEM \"gdb-target.dtd\">\n");
- string_appendf (*m_buffer, "<target>\n<architecture>%s</architecture>\n",
- tdesc_architecture_name (e));
+ add_line ("<?xml version=\"1.0\"?>");
+ add_line ("<!DOCTYPE target SYSTEM \"gdb-target.dtd\">");
+ add_line ("<target>");
+ indent (1);
+ if (tdesc_architecture_name (e))
+ add_line ("<architecture>%s</architecture>",
+ tdesc_architecture_name (e));
const char *osabi = tdesc_osabi_name (e);
if (osabi != nullptr)
- string_appendf (*m_buffer, "<osabi>%s</osabi>", osabi);
+ add_line ("<osabi>%s</osabi>", osabi);
const std::vector<tdesc_compatible_info_up> &compatible_list
= tdesc_compatible_info_list (e);
for (const auto &c : compatible_list)
- string_appendf (*m_buffer, "<compatible>%s</compatible>\n",
- tdesc_compatible_info_arch_name (c));
+ add_line ("<compatible>%s</compatible>",
+ tdesc_compatible_info_arch_name (c));
#endif
}
void print_xml_feature::visit_post (const target_desc *e)
{
- string_appendf (*m_buffer, "</target>\n");
+ indent (-1);
+ add_line ("</target>");
+}
+
+/* See gdbsupport/tdesc.h. */
+
+void
+print_xml_feature::add_line (const std::string &str)
+{
+ string_appendf (*m_buffer, "%*s", m_depth, "");
+ string_appendf (*m_buffer, "%s", str.c_str ());
+ string_appendf (*m_buffer, "\n");
+}
+
+/* See gdbsupport/tdesc.h. */
+
+void
+print_xml_feature::add_line (const char *fmt, ...)
+{
+ std::string tmp;
+
+ va_list ap;
+ va_start (ap, fmt);
+ string_vappendf (tmp, fmt, ap);
+ va_end (ap);
+ add_line (tmp);
}
{
public:
print_xml_feature (std::string *buffer_)
- : m_buffer (buffer_)
+ : m_buffer (buffer_),
+ m_depth (0)
{}
void visit_pre (const target_desc *e) override;
void visit (const tdesc_reg *reg) override;
private:
+
+ /* Called with a positive value of ADJUST when we move inside an element,
+ for example inside <target>, and with a negative value when we leave
+ the element. In this class this function does nothing, but a
+ sub-class can override this to track the current level of nesting. */
+ void indent (int adjust)
+ {
+ m_depth += (adjust * 2);
+ }
+
+ /* Functions to add lines to the output buffer M_BUFFER. Each of these
+ functions appends a newline, so don't include one in the strings being
+ passed. */
+ void add_line (const std::string &str);
+ void add_line (const char *fmt, ...);
+
+ /* The buffer we are writing too. */
std::string *m_buffer;
+
+ /* The current indentation depth. */
+ int m_depth;
};
#endif /* COMMON_TDESC_H */