This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Add printf format specifier for printing enumerator


This patch adds a format specifier to gdb's printf command, to print the
enumerator (the text label) of an enum value.  It's currently possible
to display that value using the print command, but not as part of a
formatted string.

For example, assuming we have the following enum definition:

  enum NodeType {
    ...
    NODE_INTEGER,
    ...
  };

  enum NodeType node = NODE_INTEGER;

we can display it this way:

  (gdb) printf "Visiting node of type %q\n", node
  Visiting node of type NODE_INTEGER

All the letters of "enum" are already used as format specifiers.
Actually, %m is used in GNU's extensions to printf to print errno's text
representation.  It is not used in GDB's printf, but I'd prefer to leave
the door open if we want to add it one day.  I just chose %q at random,
if you think another letter would be a better choice, I have no
objection.

In the tests, I added some enum values to enum some_volatile_enum, so
that it's not treated as an enum flag.

What do you think?

gdb/ChangeLog:

	* common/format.c (parse_format_string): Handle 'q' format specifier.
	* common/format.h (enum argclass): Add enum_arg.
	* printcmd.c (printf_enum): New function.
	(ui_printf): Handle enum_arg argclass.
	* valprint.c (val_print_enum_label): New function, factored out
	from...
	(generic_val_print_enum): ... this.
	* valprint.h (val_print_enum_label): New function declaration.
	* NEWS: Mention the new format specifier.

gdb/testsuite/ChangeLog:

	* gdb.base/printcmds.exp (test_printf): Add enum printf tests.

gdb/doc/ChangeLog:

	* gdb.texinfo (Commands for Controlled Output): Mention q
	conversion letter.
---
 gdb/NEWS                             |  6 +++
 gdb/common/format.c                  |  4 ++
 gdb/common/format.h                  |  2 +-
 gdb/doc/gdb.texinfo                  |  3 ++
 gdb/printcmd.c                       | 20 +++++++++
 gdb/testsuite/gdb.base/printcmds.c   |  2 +-
 gdb/testsuite/gdb.base/printcmds.exp | 10 +++++
 gdb/valprint.c                       | 87 +++++++++++++++++++-----------------
 gdb/valprint.h                       |  3 ++
 9 files changed, 95 insertions(+), 42 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index 962eae4..ce9f7b0 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,12 @@
 
 *** Changes since GDB 7.10
 
+* GDB's printf command now has a format specifier (%q) to display the
+  textual label (enumerator) of an enumeration value.  For example:
+
+    (gdb) printf "Visiting node of type %q\n", node
+    Visiting node of type NODE_INTEGER
+
 * GDB now supports debugging kernel-based threads on FreeBSD.
 
 * Per-inferior thread numbers
diff --git a/gdb/common/format.c b/gdb/common/format.c
index fbb82fd..f90893e 100644
--- a/gdb/common/format.c
+++ b/gdb/common/format.c
@@ -294,6 +294,10 @@ parse_format_string (const char **arg)
 	  case '\0':
 	    error (_("Incomplete format specifier at end of format string"));
 
+	  case 'q':
+	    this_argclass = enum_arg;
+	    break;
+
 	  default:
 	    error (_("Unrecognized format specifier '%c' in printf"), *f);
 	  }
diff --git a/gdb/common/format.h b/gdb/common/format.h
index cb56a2c..884403e 100644
--- a/gdb/common/format.h
+++ b/gdb/common/format.h
@@ -35,7 +35,7 @@ enum argclass
     literal_piece,
     int_arg, long_arg, long_long_arg, ptr_arg,
     string_arg, wide_string_arg, wide_char_arg,
-    double_arg, long_double_arg, decfloat_arg
+    double_arg, long_double_arg, decfloat_arg, enum_arg
   };
 
 /* A format piece is a section of the format string that may include a
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 2d09d13..183bd9e 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -24343,6 +24343,9 @@ sequences, such as @code{\n}, @samp{\t}, @samp{\\}, @samp{\"},
 single character.  Octal and hexadecimal escape sequences are not
 supported.
 
+@code{printf} supports the @samp{q} conversion letter to print the textual
+label (enumerator) of a C enumeration value.
+
 Additionally, @code{printf} supports conversion specifications for DFP
 (@dfn{Decimal Floating Point}) types using the following length modifiers
 together with a floating point specifier.
diff --git a/gdb/printcmd.c b/gdb/printcmd.c
index f5c4211..e4637df 100644
--- a/gdb/printcmd.c
+++ b/gdb/printcmd.c
@@ -2258,6 +2258,23 @@ printf_pointer (struct ui_file *stream, const char *format,
     }
 }
 
+static void
+printf_enum (struct ui_file *stream, struct value *value)
+{
+  struct type *type = check_typedef (value_type (value));
+
+  if (TYPE_CODE (type) == TYPE_CODE_ENUM)
+    {
+      val_print_enum_label (type, value_contents_for_printing (value),
+			    value_embedded_offset (value), stream);
+    }
+  else
+    {
+      error (_("\
+Value given for enum format specifier (%%q) is not of an enum type."));
+    }
+}
+
 /* printf "printf format string" ARG to STREAM.  */
 
 static void
@@ -2446,6 +2463,9 @@ ui_printf (const char *arg, struct ui_file *stream)
 	  case ptr_arg:
 	    printf_pointer (stream, current_substring, val_args[i]);
 	    break;
+	  case enum_arg:
+	    printf_enum (stream, val_args[i]);
+	    break;
 	  case literal_piece:
 	    /* Print a portion of the format string that has no
 	       directives.  Note that this will not include any
diff --git a/gdb/testsuite/gdb.base/printcmds.c b/gdb/testsuite/gdb.base/printcmds.c
index 57e04e6..bc7c981 100644
--- a/gdb/testsuite/gdb.base/printcmds.c
+++ b/gdb/testsuite/gdb.base/printcmds.c
@@ -90,7 +90,7 @@ struct some_arrays {
 
 struct some_arrays *parrays = &arrays;
 
-enum some_volatile_enum { enumvolval1, enumvolval2 };
+enum some_volatile_enum { enumvolval1, enumvolval2, enumvolval3, enumvolval4 };
 
 /* A volatile enum variable whose name is the same as the enumeration
    name.  See PR11827.  */
diff --git a/gdb/testsuite/gdb.base/printcmds.exp b/gdb/testsuite/gdb.base/printcmds.exp
index 3c78a53..4254581d 100644
--- a/gdb/testsuite/gdb.base/printcmds.exp
+++ b/gdb/testsuite/gdb.base/printcmds.exp
@@ -763,6 +763,16 @@ proc test_printf {} {
 	"" \
 	"create hibob command"
     gdb_test "hibob" "hi bob zzz.*y" "run hibob command"
+
+    # Test for enum printing (%q) in printf
+    gdb_test "printf \"%q\\n\", some_volatile_enum" "enumvolval1"
+    gdb_test "printf \"%q\\n\", enumvolval2" "enumvolval2"
+    gdb_test "printf \"%q\\n\", (enum some_volatile_enum) 1" "enumvolval2"
+    gdb_test "printf \"%q\\n\", (enum some_volatile_enum) 111" "111"
+    gdb_test "printf \"%q\\n\", three" "\\(ONE | TWO\\)"
+    gdb_test "printf \"%q\\n\", (enum flag_enum) 8" "\\(unknown: 8\\)"
+
+    gdb_test "printf \"%q\\n\", 2" "Value given for enum format specifier \\(%q\\) is not of an enum type."
 }
 
 #Test printing DFP values with printf
diff --git a/gdb/valprint.c b/gdb/valprint.c
index a9b03ec..fde48b4 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -526,35 +526,23 @@ generic_val_print_ref (struct type *type, const gdb_byte *valaddr,
     }
 }
 
-/* generic_val_print helper for TYPE_CODE_ENUM.  */
-
-static void
-generic_val_print_enum (struct type *type, const gdb_byte *valaddr,
-			int embedded_offset, struct ui_file *stream,
-			const struct value *original_value,
-			const struct value_print_options *options)
+void
+val_print_enum_label (struct type *type, const gdb_byte *valaddr,
+		      int embedded_offset, struct ui_file *stream)
 {
-  unsigned int i;
-  unsigned int len;
-  LONGEST val;
+  int len = TYPE_NFIELDS (type);
+  int i;
   struct gdbarch *gdbarch = get_type_arch (type);
   int unit_size = gdbarch_addressable_memory_unit_size (gdbarch);
+  LONGEST val = unpack_long (type, valaddr + embedded_offset * unit_size);
 
-  if (options->format)
-    {
-      val_print_scalar_formatted (type, valaddr, embedded_offset,
-				  original_value, options, 0, stream);
-      return;
-    }
-  len = TYPE_NFIELDS (type);
-  val = unpack_long (type, valaddr + embedded_offset * unit_size);
   for (i = 0; i < len; i++)
     {
       QUIT;
       if (val == TYPE_FIELD_ENUMVAL (type, i))
-	{
-	  break;
-	}
+      {
+	break;
+      }
     }
   if (i < len)
     {
@@ -565,31 +553,31 @@ generic_val_print_enum (struct type *type, const gdb_byte *valaddr,
       int first = 1;
 
       /* We have a "flag" enum, so we try to decompose it into
-	 pieces as appropriate.  A flag enum has disjoint
-	 constants by definition.  */
+       pieces as appropriate.  A flag enum has disjoint
+       constants by definition.  */
       fputs_filtered ("(", stream);
       for (i = 0; i < len; ++i)
-	{
-	  QUIT;
+      {
+	QUIT;
 
-	  if ((val & TYPE_FIELD_ENUMVAL (type, i)) != 0)
-	    {
-	      if (!first)
-		fputs_filtered (" | ", stream);
-	      first = 0;
+	if ((val & TYPE_FIELD_ENUMVAL (type, i)) != 0)
+	  {
+	    if (!first)
+	      fputs_filtered (" | ", stream);
+	    first = 0;
 
-	      val &= ~TYPE_FIELD_ENUMVAL (type, i);
-	      fputs_filtered (TYPE_FIELD_NAME (type, i), stream);
-	    }
-	}
+	    val &= ~TYPE_FIELD_ENUMVAL (type, i);
+	    fputs_filtered (TYPE_FIELD_NAME (type, i), stream);
+	  }
+      }
 
       if (first || val != 0)
-	{
-	  if (!first)
-	    fputs_filtered (" | ", stream);
-	  fputs_filtered ("unknown: ", stream);
-	  print_longest (stream, 'd', 0, val);
-	}
+      {
+	if (!first)
+	  fputs_filtered (" | ", stream);
+	fputs_filtered ("unknown: ", stream);
+	print_longest (stream, 'd', 0, val);
+      }
 
       fputs_filtered (")", stream);
     }
@@ -597,6 +585,25 @@ generic_val_print_enum (struct type *type, const gdb_byte *valaddr,
     print_longest (stream, 'd', 0, val);
 }
 
+/* generic_val_print helper for TYPE_CODE_ENUM.  */
+
+static void
+generic_val_print_enum (struct type *type, const gdb_byte *valaddr,
+			int embedded_offset, struct ui_file *stream,
+			const struct value *original_value,
+			const struct value_print_options *options)
+{
+  if (options->format)
+    {
+      val_print_scalar_formatted (type, valaddr, embedded_offset,
+				  original_value, options, 0, stream);
+    }
+  else
+    {
+      val_print_enum_label (type, valaddr, embedded_offset, stream);
+    }
+}
+
 /* generic_val_print helper for TYPE_CODE_FLAGS.  */
 
 static void
diff --git a/gdb/valprint.h b/gdb/valprint.h
index bb03024..97a22d5 100644
--- a/gdb/valprint.h
+++ b/gdb/valprint.h
@@ -170,6 +170,9 @@ extern void val_print_unavailable (struct ui_file *stream);
 
 extern void val_print_invalid_address (struct ui_file *stream);
 
+extern void val_print_enum_label (struct type *type, const gdb_byte *valaddr,
+				  int embedded_offset, struct ui_file *stream);
+
 /* An instance of this is passed to generic_val_print and describes
    some language-specific ways to print things.  */
 
-- 
2.7.0


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]