public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
From: Tom Tromey <tom@tromey.com>
To: gdb-patches@sourceware.org
Cc: Tom Tromey <tom@tromey.com>
Subject: [RFA 2/2] Support ptype/o in Rust
Date: Sat, 23 Jun 2018 20:22:00 -0000	[thread overview]
Message-ID: <20180623202227.17259-3-tom@tromey.com> (raw)
In-Reply-To: <20180623202227.17259-1-tom@tromey.com>

This adds support for ptype/o to the Rust language code.

By default, the Rust compiler reorders fields to reduce padding.  So,
the Rust language code sorts the fields by offset before printing.
This may yield somewhat odd-looking results, but it is faithful to
"what really happens", and might be useful when doing lower-level
debugging.

The reordering can be disabled using #[repr(c)]; ptype/o might be more
useful in this case.

gdb/ChangeLog
2018-06-23  Tom Tromey  <tom@tromey.com>

	PR rust/22574:
	* typeprint.c (whatis_exp): Allow ptype/o for Rust.
	* rust-lang.c (rust_print_struct_def): Add podata parameter.
	Update.
	(rust_internal_print_type): Add podata parameter.
	(rust_print_type): Update.

gdb/testsuite/ChangeLog
2018-06-23  Tom Tromey  <tom@tromey.com>

	PR rust/22574:
	* gdb.rust/simple.exp (test_one_slice): Add ptype/o tests.
	* gdb.rust/simple.rs (struct SimpleLayout): New.
---
 gdb/ChangeLog                     |  9 +++++
 gdb/rust-lang.c                   | 82 ++++++++++++++++++++++++++++++---------
 gdb/testsuite/ChangeLog           |  6 +++
 gdb/testsuite/gdb.rust/simple.exp | 18 +++++++++
 gdb/testsuite/gdb.rust/simple.rs  |  8 ++++
 gdb/typeprint.c                   |  3 +-
 6 files changed, 106 insertions(+), 20 deletions(-)

diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c
index d9807d0ac1a..af1144dde07 100644
--- a/gdb/rust-lang.c
+++ b/gdb/rust-lang.c
@@ -31,8 +31,10 @@
 #include "objfiles.h"
 #include "psymtab.h"
 #include "rust-lang.h"
+#include "typeprint.h"
 #include "valprint.h"
 #include "varobj.h"
+#include <algorithm>
 #include <string>
 #include <vector>
 
@@ -616,14 +618,14 @@ static void
 rust_internal_print_type (struct type *type, const char *varstring,
 			  struct ui_file *stream, int show, int level,
 			  const struct type_print_options *flags,
-			  bool for_rust_enum);
+			  bool for_rust_enum, print_offset_data *podata);
 
 /* Print a struct or union typedef.  */
 static void
 rust_print_struct_def (struct type *type, const char *varstring,
 		       struct ui_file *stream, int show, int level,
 		       const struct type_print_options *flags,
-		       bool for_rust_enum)
+		       bool for_rust_enum, print_offset_data *podata)
 {
   /* Print a tuple type simply.  */
   if (rust_tuple_type_p (type))
@@ -636,6 +638,13 @@ rust_print_struct_def (struct type *type, const char *varstring,
   if (TYPE_N_BASECLASSES (type) > 0)
     c_print_type (type, varstring, stream, show, level, flags);
 
+  if (flags->print_offsets)
+    {
+      /* Temporarily bump the level so that the output lines up
+	 correctly.  */
+      level += 2;
+    }
+
   /* Compute properties of TYPE here because, in the enum case, the
      rest of the code ends up looking only at the variant part.  */
   const char *tagname = TYPE_NAME (type);
@@ -674,16 +683,41 @@ rust_print_struct_def (struct type *type, const char *varstring,
 
   if (TYPE_NFIELDS (type) == 0 && !is_tuple)
     return;
-  if (for_rust_enum)
+  if (for_rust_enum && !flags->print_offsets)
     fputs_filtered (is_tuple_struct ? "(" : "{", stream);
   else
     fputs_filtered (is_tuple_struct ? " (\n" : " {\n", stream);
 
+  // When printing offsets, we rearrange the fields into storage
+  // order.  This lets us show holes more clearly.  We work using
+  // field indices here because it simplifies calls to
+  // print_offset_data::update below.
+  std::vector<int> fields;
   for (int i = 0; i < TYPE_NFIELDS (type); ++i)
     {
-      QUIT;
       if (field_is_static (&TYPE_FIELD (type, i)))
 	continue;
+      if (is_enum && i == enum_discriminant_index)
+	continue;
+      fields.push_back (i);
+    }
+  if (flags->print_offsets)
+    std::sort (fields.begin (), fields.end (),
+	       [&] (int a, int b)
+	       {
+		 return (TYPE_FIELD_BITPOS (type, a)
+			 < TYPE_FIELD_BITPOS (type, b));
+	       });
+
+  for (int i : fields)
+    {
+      QUIT;
+
+      gdb_assert (!field_is_static (&TYPE_FIELD (type, i)));
+      gdb_assert (! (is_enum && i == enum_discriminant_index));
+
+      if (flags->print_offsets)
+	podata->update (type, i, stream);
 
       /* We'd like to print "pub" here as needed, but rustc
 	 doesn't emit the debuginfo, and our types don't have
@@ -691,27 +725,35 @@ rust_print_struct_def (struct type *type, const char *varstring,
 
       /* For a tuple struct we print the type but nothing
 	 else.  */
-      if (!for_rust_enum)
+      if (!for_rust_enum || flags->print_offsets)
 	print_spaces_filtered (level + 2, stream);
       if (is_enum)
-	{
-	  if (i == enum_discriminant_index)
-	    continue;
-	  fputs_filtered (TYPE_FIELD_NAME (type, i), stream);
-	}
+	fputs_filtered (TYPE_FIELD_NAME (type, i), stream);
       else if (!is_tuple_struct)
 	fprintf_filtered (stream, "%s: ", TYPE_FIELD_NAME (type, i));
 
       rust_internal_print_type (TYPE_FIELD_TYPE (type, i), NULL,
 				stream, (is_enum ? show : show - 1),
-				level + 2, flags, is_enum);
-      if (!for_rust_enum)
+				level + 2, flags, is_enum, podata);
+      if (!for_rust_enum || flags->print_offsets)
 	fputs_filtered (",\n", stream);
+      /* Note that this check of "I" is ok because we only sorted the
+	 fields by offset when print_offsets was set, so we won't take
+	 this branch in that case.  */
       else if (i + 1 < TYPE_NFIELDS (type))
 	fputs_filtered (", ", stream);
     }
 
-  if (!for_rust_enum)
+  if (flags->print_offsets)
+    {
+      /* Undo the temporary level increase we did above.  */
+      level -= 2;
+      podata->finish (type, level, stream);
+      print_spaces_filtered (print_offset_data::indentation, stream);
+      if (level == 0)
+	print_spaces_filtered (2, stream);
+    }
+  if (!for_rust_enum || flags->print_offsets)
     print_spaces_filtered (level, stream);
   fputs_filtered (is_tuple_struct ? ")" : "}", stream);
 }
@@ -735,7 +777,7 @@ static void
 rust_internal_print_type (struct type *type, const char *varstring,
 			  struct ui_file *stream, int show, int level,
 			  const struct type_print_options *flags,
-			  bool for_rust_enum)
+			  bool for_rust_enum, print_offset_data *podata)
 {
   int i;
 
@@ -778,7 +820,7 @@ rust_internal_print_type (struct type *type, const char *varstring,
 	  if (i > 0)
 	    fputs_filtered (", ", stream);
 	  rust_internal_print_type (TYPE_FIELD_TYPE (type, i), "", stream,
-				    -1, 0, flags, false);
+				    -1, 0, flags, false, podata);
 	}
       fputs_filtered (")", stream);
       /* If it returns unit, we can omit the return type.  */
@@ -786,7 +828,7 @@ rust_internal_print_type (struct type *type, const char *varstring,
         {
           fputs_filtered (" -> ", stream);
           rust_internal_print_type (TYPE_TARGET_TYPE (type), "", stream,
-				    -1, 0, flags, false);
+				    -1, 0, flags, false, podata);
         }
       break;
 
@@ -796,7 +838,8 @@ rust_internal_print_type (struct type *type, const char *varstring,
 
 	fputs_filtered ("[", stream);
 	rust_internal_print_type (TYPE_TARGET_TYPE (type), NULL,
-				  stream, show - 1, level, flags, false);
+				  stream, show - 1, level, flags, false,
+				  podata);
 
 	if (TYPE_HIGH_BOUND_KIND (TYPE_INDEX_TYPE (type)) == PROP_LOCEXPR
 	    || TYPE_HIGH_BOUND_KIND (TYPE_INDEX_TYPE (type)) == PROP_LOCLIST)
@@ -811,7 +854,7 @@ rust_internal_print_type (struct type *type, const char *varstring,
     case TYPE_CODE_UNION:
     case TYPE_CODE_STRUCT:
       rust_print_struct_def (type, varstring, stream, show, level, flags,
-			     for_rust_enum);
+			     for_rust_enum, podata);
       break;
 
     case TYPE_CODE_ENUM:
@@ -856,8 +899,9 @@ rust_print_type (struct type *type, const char *varstring,
 		 struct ui_file *stream, int show, int level,
 		 const struct type_print_options *flags)
 {
+  print_offset_data podata;
   rust_internal_print_type (type, varstring, stream, show, level,
-			    flags, false);
+			    flags, false, &podata);
 }
 
 \f
diff --git a/gdb/testsuite/gdb.rust/simple.exp b/gdb/testsuite/gdb.rust/simple.exp
index ba90e061ce3..20fe8dd0a88 100644
--- a/gdb/testsuite/gdb.rust/simple.exp
+++ b/gdb/testsuite/gdb.rust/simple.exp
@@ -285,6 +285,24 @@ gdb_test "print parametrized" \
 
 gdb_test "print u" " = simple::Union {f1: -1, f2: 255}"
 
+gdb_test_sequence "ptype/o Union" "" {
+    "/\\* offset    |  size \\*/  type = union simple::Union {"
+    "/\\*                 1 \\*/    f1: i8,"
+    "/\\*                 1 \\*/    f2: u8,"
+    ""
+    "                           /\\* total size \\(bytes\\):    1 \\*/"
+    "                         }"
+}
+
+gdb_test_sequence "ptype/o SimpleLayout" "" {
+    "/\\* offset    |  size \\*/  type = struct simple::SimpleLayout {"
+    "/\\*    0      |     2 \\*/    f1: u16,"
+    "/\\*    2      |     2 \\*/    f2: u16,"
+    ""
+    "                           /\\* total size \\(bytes\\):    4 \\*/"
+    "                         }"
+}
+
 load_lib gdb-python.exp
 if {[skip_python_tests]} {
     continue
diff --git a/gdb/testsuite/gdb.rust/simple.rs b/gdb/testsuite/gdb.rust/simple.rs
index e5bbe521226..9d89361b753 100644
--- a/gdb/testsuite/gdb.rust/simple.rs
+++ b/gdb/testsuite/gdb.rust/simple.rs
@@ -85,6 +85,13 @@ union Union {
     f2: u8,
 }
 
+// A simple structure whose layout won't be changed by the compiler,
+// so that ptype/o testing will work on any platform.
+struct SimpleLayout {
+    f1: u16,
+    f2: u16
+}
+
 fn main () {
     let a = ();
     let b : [i32; 0] = [];
@@ -159,6 +166,7 @@ fn main () {
     };
 
     let u = Union { f2: 255 };
+    let v = SimpleLayout { f1: 8, f2: 9 };
 
     println!("{}, {}", x.0, x.1);        // set breakpoint here
     println!("{}", diff2(92, 45));
diff --git a/gdb/typeprint.c b/gdb/typeprint.c
index 66ba0a87c6a..c6a404665ba 100644
--- a/gdb/typeprint.c
+++ b/gdb/typeprint.c
@@ -488,7 +488,8 @@ whatis_exp (const char *exp, int show)
 		       feature.  */
 		    if (show > 0
 			&& (current_language->la_language == language_c
-			    || current_language->la_language == language_cplus))
+			    || current_language->la_language == language_cplus
+			    || current_language->la_language == language_rust))
 		      {
 			flags.print_offsets = 1;
 			flags.print_typedefs = 0;
-- 
2.13.6

  parent reply	other threads:[~2018-06-23 20:22 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-06-23 20:22 [RFA 0/2] " Tom Tromey
2018-06-23 20:22 ` [RFA 1/2] Move ptype/o printing code to typeprint.c Tom Tromey
2018-06-23 23:51   ` Sergio Durigan Junior
2018-06-26 20:54     ` Tom Tromey
2018-06-23 20:22 ` Tom Tromey [this message]
2018-06-26 19:08   ` [RFA 2/2] Support ptype/o in Rust Simon Marchi
2018-06-26 20:55     ` Tom Tromey
2018-06-23 23:44 ` [RFA 0/2] " Sergio Durigan Junior
2018-06-27  2:59   ` Tom Tromey

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180623202227.17259-3-tom@tromey.com \
    --to=tom@tromey.com \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).