diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 0784e823e21..57bd32397d7 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,12 @@ +2020-06-23 Andrew Burgess + + * 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 * target-descriptions.c (class tdesc_compatible_info): New class. diff --git a/gdb/NEWS b/gdb/NEWS index f7585133c51..a116d62bca3 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -79,6 +79,12 @@ tui new-layout NAME WINDOW WEIGHT [WINDOW WEIGHT]... 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...] diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index fe6bfe74523..4b1a4c01f9f 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,8 @@ +2020-06-23 Andrew Burgess + + * gdb.texinfo (Maintenance Commands): Document new 'maint print + xml-desc' command. + 2020-06-22 Philippe Waroquiers * gdb.texinfo (Command aliases default args): New node documenting diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 7f572c37c5c..7f8c77a77f2 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -38501,6 +38501,15 @@ The created source file is built into @value{GDBN} when @value{GDBN} is 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} diff --git a/gdb/target-descriptions.c b/gdb/target-descriptions.c index 1937e7ca4a7..1abdf51ec72 100644 --- a/gdb/target-descriptions.c +++ b/gdb/target-descriptions.c @@ -662,7 +662,9 @@ tdesc_architecture (const struct target_desc *target_desc) 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. */ @@ -1755,6 +1757,36 @@ maint_print_c_tdesc_cmd (const char *args, int from_tty) } } +/* 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). */ @@ -1892,6 +1924,11 @@ Print the current target description as a C source file."), &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\ diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 0864d7522a4..26284dabb25 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,11 @@ +2020-06-23 Andrew Burgess + + * 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 * lib/completion-support.exp (test_gdb_completion_offers_commands): diff --git a/gdb/testsuite/gdb.xml/maint-xml-dump-01.xml b/gdb/testsuite/gdb.xml/maint-xml-dump-01.xml new file mode 100644 index 00000000000..dd4f9251676 --- /dev/null +++ b/gdb/testsuite/gdb.xml/maint-xml-dump-01.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/gdb/testsuite/gdb.xml/maint-xml-dump-02.xml b/gdb/testsuite/gdb.xml/maint-xml-dump-02.xml new file mode 100644 index 00000000000..c192294ca10 --- /dev/null +++ b/gdb/testsuite/gdb.xml/maint-xml-dump-02.xml @@ -0,0 +1,27 @@ + + Solaris + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb/testsuite/gdb.xml/maint-xml-dump.exp b/gdb/testsuite/gdb.xml/maint-xml-dump.exp new file mode 100644 index 00000000000..8ccfbb5e687 --- /dev/null +++ b/gdb/testsuite/gdb.xml/maint-xml-dump.exp @@ -0,0 +1,124 @@ +# 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 . + +# 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 and 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): +# | +# Will actually look for the following output: +# | +# +# 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 {} + set doc_type_line {} + + set linenum 0 + set ifd [open "$filename" r] + while {[gets $ifd line] >= 0} { + incr linenum + + # The 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 then add a default + # DOCTYPE line, otherwise, if the XML file includes a DOCTYPE + # line, use that. + if {$doc_type_line != "" } { + if {[regexp {^[ \t]*} $line]} { + set pattern [multi_line $pattern \ + [string_to_regexp $doc_type_line]] + set doc_type_line "" + } 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 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]" +} diff --git a/gdb/testsuite/gdb.xml/tdesc-reload.c b/gdb/testsuite/gdb.xml/tdesc-reload.c new file mode 100644 index 00000000000..f4825c8a7c1 --- /dev/null +++ b/gdb/testsuite/gdb.xml/tdesc-reload.c @@ -0,0 +1,22 @@ +/* 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 . */ + +int +main () +{ + return 0; +} diff --git a/gdb/testsuite/gdb.xml/tdesc-reload.exp b/gdb/testsuite/gdb.xml/tdesc-reload.exp new file mode 100644 index 00000000000..a671297f821 --- /dev/null +++ b/gdb/testsuite/gdb.xml/tdesc-reload.exp @@ -0,0 +1,83 @@ +# 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 . + +# 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" diff --git a/gdbsupport/ChangeLog b/gdbsupport/ChangeLog index 2e5cbba01c6..b2fbc56b1b9 100644 --- a/gdbsupport/ChangeLog +++ b/gdbsupport/ChangeLog @@ -1,3 +1,16 @@ +2020-06-23 Andrew Burgess + + * 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 * tdesc.cc (print_xml_feature::visit_pre): Print compatible diff --git a/gdbsupport/tdesc.cc b/gdbsupport/tdesc.cc index 63f41cbf677..624588b6563 100644 --- a/gdbsupport/tdesc.cc +++ b/gdbsupport/tdesc.cc @@ -294,12 +294,14 @@ tdesc_add_enum_value (tdesc_type_with_fields *type, int value, void print_xml_feature::visit_pre (const tdesc_feature *e) { - string_appendf (*m_buffer, "\n", e->name.c_str ()); + add_line ("", e->name.c_str ()); + indent (1); } void print_xml_feature::visit_post (const tdesc_feature *e) { - string_appendf (*m_buffer, "\n"); + indent (-1); + add_line (""); } void print_xml_feature::visit (const tdesc_type_builtin *t) @@ -309,8 +311,8 @@ void print_xml_feature::visit (const tdesc_type_builtin *t) void print_xml_feature::visit (const tdesc_type_vector *t) { - string_appendf (*m_buffer, "\n", - t->name.c_str (), t->element_type->name.c_str (), t->count); + add_line ("", + t->name.c_str (), t->element_type->name.c_str (), t->count); } void print_xml_feature::visit (const tdesc_type_with_fields *t) @@ -319,7 +321,9 @@ 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 ()); @@ -328,33 +332,37 @@ void print_xml_feature::visit (const tdesc_type_with_fields *t) 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, " \n", - f.type->name.c_str ()); - else - string_appendf (*m_buffer, "start=\"%d\" end=\"%d\"/>\n", f.start, + tmp.clear (); + string_appendf (tmp, " ", + 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, " \n", - f.name.c_str (), f.start); + add_line (" ", + 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, " \n", - f.name.c_str (), f.type->name.c_str ()); + add_line (" ", + f.name.c_str (), f.type->name.c_str ()); break; default: @@ -362,46 +370,78 @@ void print_xml_feature::visit (const tdesc_type_with_fields *t) t->name.c_str ()); } - string_appendf (*m_buffer, "\n", types[t->kind - TDESC_TYPE_STRUCT]); + add_line ("", 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, "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, "\n"); - string_appendf (*m_buffer, "\n"); - string_appendf (*m_buffer, "\n%s\n", - tdesc_architecture_name (e)); + add_line (""); + add_line (""); + add_line (""); + indent (1); + if (tdesc_architecture_name (e)) + add_line ("%s", + tdesc_architecture_name (e)); const char *osabi = tdesc_osabi_name (e); if (osabi != nullptr) - string_appendf (*m_buffer, "%s", osabi); + add_line ("%s", osabi); const std::vector &compatible_list = tdesc_compatible_info_list (e); for (const auto &c : compatible_list) - string_appendf (*m_buffer, "%s\n", - tdesc_compatible_info_arch_name (c)); + add_line ("%s", + tdesc_compatible_info_arch_name (c)); #endif } void print_xml_feature::visit_post (const target_desc *e) { - string_appendf (*m_buffer, "\n"); + indent (-1); + add_line (""); +} + +/* 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); } diff --git a/gdbsupport/tdesc.h b/gdbsupport/tdesc.h index 0cdcf56346c..73caf24536f 100644 --- a/gdbsupport/tdesc.h +++ b/gdbsupport/tdesc.h @@ -410,7 +410,8 @@ class print_xml_feature : public tdesc_element_visitor { public: print_xml_feature (std::string *buffer_) - : m_buffer (buffer_) + : m_buffer (buffer_), + m_depth (0) {} void visit_pre (const target_desc *e) override; @@ -423,7 +424,27 @@ public: void visit (const tdesc_reg *reg) override; private: + + /* Called with a positive value of ADJUST when we move inside an element, + for example inside , 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 */