public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* RFC: enum pretty-printing support
@ 2012-01-10 20:26 Tom Tromey
  2012-01-10 20:28 ` Pedro Alves
                   ` (2 more replies)
  0 siblings, 3 replies; 13+ messages in thread
From: Tom Tromey @ 2012-01-10 20:26 UTC (permalink / raw)
  To: gdb-patches

I'd appreciate comments on this patch.

This adds a new helper class to gdb.printing to handle printing of enum
values which are or'd together.  E.g.:

print (enum flag_enum) (FLAG_1 | FLAG_3)
$8 = 0x5 [FLAG_1 | FLAG_3]

Tom

b/gdb/ChangeLog:
2012-01-10  Tom Tromey  <tromey@redhat.com>

	PR python/13281:
	* python/lib/gdb/printing.py (_EnumInstance): New class.
	(EnumerationPrinter): Likewise.

b/gdb/doc/ChangeLog:
2012-01-10  Tom Tromey  <tromey@redhat.com>

	* gdb.texinfo (gdb.printing): Document EnumerationPrinter.

b/gdb/testsuite/ChangeLog:
2012-01-10  Tom Tromey  <tromey@redhat.com>

	* gdb.python/py-pp-maint.py (build_pretty_printer): Instantiate
	EnumerationPrinter.
	* gdb.python/py-pp-maint.exp: Add tests for EnumerationPrinter.
	* gdb.python/py-pp-maint.c (enum flag_enum): New.
	(fval): New global.

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 853692c..3896bf1 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -24380,6 +24380,13 @@ Utility class for handling multiple printers, all recognized via
 regular expressions.
 @xref{Writing a Pretty-Printer}, for an example.
 
+@item EnumerationPrinter (@var{name})
+A pretty-printer which handles printing of @code{enum} values.  Unlike
+@value{GDBN}'s built-in @code{enum} printing, this printer handles the
+case where enumerators are or'd together to create a value.
+@var{name} is the name of the printer and also the name of the
+@code{enum} type to look up.
+
 @item register_pretty_printer (@var{obj}, @var{printer}, @var{replace}=False)
 Register @var{printer} with the pretty-printer list of @var{obj}.
 If @var{replace} is @code{True} then any existing copy of the printer
diff --git a/gdb/python/lib/gdb/printing.py b/gdb/python/lib/gdb/printing.py
index a030827..52089a3 100644
--- a/gdb/python/lib/gdb/printing.py
+++ b/gdb/python/lib/gdb/printing.py
@@ -206,3 +206,47 @@ class RegexpCollectionPrettyPrinter(PrettyPrinter):
 
         # Cannot find a pretty printer.  Return None.
         return None
+
+# A helper class for printing enum types.  This class is instantiated
+# with a list of enumerators to print a particular Value.
+class _EnumInstance:
+    def __init__(self, enumerators, val):
+        self.enumerators = enumerators
+        self.val = val
+
+    def to_string(self):
+        flag_list = []
+        v = long(self.val)
+        for (e_name, e_value) in self.enumerators:
+            if v & e_value != 0:
+                flag_list.append(e_name)
+                # We assume the enumerators are exclusive.
+                v = v & ~e_value
+        if v != 0:
+            # Leftover value.
+            flag_list.append('<unknown: 0x%x>' % v)
+        return "0x%x [%s]" % (self.val, " | ".join(flag_list))
+
+class EnumerationPrinter(PrettyPrinter):
+    """A pretty-printer which can be used to print a flag-style enumeration.
+    A flag-style enumeration is one where the enumerators are or'd
+    together to create values.  The new printer will print these
+    symbolically using '|' notation.  The printer must be registered
+    manually."""
+
+    def __init__(self, enum_type):
+        super(EnumerationPrinter, self).__init__(enum_type)
+        self.initialized = False
+
+    def __call__(self, val):
+        if not self.initialized:
+            self.initialized = True
+            flags = gdb.lookup_type(self.name)
+            self.enumerators = []
+            for field in flags.fields():
+                self.enumerators.append((field.name, field.bitpos))
+
+        if self.enabled:
+            return _EnumInstance(self.enumerators, val)
+        else:
+            return None
diff --git a/gdb/testsuite/gdb.python/py-pp-maint.c b/gdb/testsuite/gdb.python/py-pp-maint.c
index d9b0fa0..3e4aa24 100644
--- a/gdb/testsuite/gdb.python/py-pp-maint.c
+++ b/gdb/testsuite/gdb.python/py-pp-maint.c
@@ -17,6 +17,15 @@
 
 #include <string.h>
 
+enum flag_enum
+  {
+    FLAG_1 = 1,
+    FLAG_2 = 2,
+    FLAG_3 = 4
+  };
+
+enum flag_enum fval;
+
 struct function_lookup_test
 {
   int x,y;
diff --git a/gdb/testsuite/gdb.python/py-pp-maint.exp b/gdb/testsuite/gdb.python/py-pp-maint.exp
index ce88ca5..cc8777a 100644
--- a/gdb/testsuite/gdb.python/py-pp-maint.exp
+++ b/gdb/testsuite/gdb.python/py-pp-maint.exp
@@ -74,23 +74,25 @@ gdb_test "print flt" " = x=<42> y=<43>" \
 gdb_test "print ss" " = a=<a=<1> b=<$hex>> b=<a=<2> b=<$hex>>" \
     "print ss enabled #1"
 
+set num_pp 6
+
 gdb_test "disable pretty-printer" \
-    "5 printers disabled.*0 of 5 printers enabled"
+    "$num_pp printers disabled.*0 of $num_pp printers enabled"
 
 gdb_test "enable pretty-printer" \
-    "5 printers enabled.*5 of 5 printers enabled"
+    "$num_pp printers enabled.*$num_pp of $num_pp printers enabled"
 
 gdb_test "disable pretty-printer global" \
-    "5 printers disabled.*0 of 5 printers enabled"
+    "$num_pp printers disabled.*0 of $num_pp printers enabled"
 
 gdb_test "enable pretty-printer" \
-    "5 printers enabled.*5 of 5 printers enabled"
+    "$num_pp printers enabled.*$num_pp of $num_pp printers enabled"
 
 gdb_test "disable pretty-printer global lookup_function_lookup_test" \
-    "1 printer disabled.*4 of 5 printers enabled"
+    "1 printer disabled.*[expr $num_pp - 1] of $num_pp printers enabled"
 
 gdb_test "disable pretty-printer global pp-test;.*" \
-    "4 printers disabled.*0 of 5 printers enabled"
+    "[expr $num_pp - 1] printers disabled.*0 of $num_pp printers enabled"
 
 gdb_test "info pretty-printer global .*function" \
     {.*function_lookup_test \[disabled\].*}
@@ -105,19 +107,22 @@ gdb_test "print ss" " = {a = {a = 1, b = $hex}, b = {a = 2, b = $hex}}" \
     "print ss disabled"
 
 gdb_test "enable pretty-printer global lookup_function_lookup_test" \
-    "1 printer enabled.*1 of 5 printers enabled"
+    "1 printer enabled.*1 of $num_pp printers enabled"
 
 # This doesn't enable any printers because each subprinter in the collection
 # is still individually disabled.  But this is still needed, to enable the
 # collection itself.
 gdb_test "enable pretty-printer global pp-test" \
-    "0 printers enabled.*1 of 5 printers enabled"
+    "0 printers enabled.*1 of $num_pp printers enabled"
 
 gdb_test "enable pretty-printer global pp-test;.*ss.*" \
-    "2 printers enabled.*3 of 5 printers enabled"
+    "2 printers enabled.*[expr $num_pp - 3] of $num_pp printers enabled"
 
 gdb_test "enable pretty-printer global pp-test;.*s.*" \
-    "2 printers enabled.*5 of 5 printers enabled"
+    "2 printers enabled.*[expr $num_pp - 1] of $num_pp printers enabled"
+
+gdb_test "enable pretty-printer global pp-test;.*" \
+    "1 printer enabled.*$num_pp of $num_pp printers enabled"
 
 gdb_test "info pretty-printer" \
     {.*function_lookup_test.*pp-test.*struct ss.*}
@@ -127,3 +132,18 @@ gdb_test "print flt" " = x=<42> y=<43>" \
 
 gdb_test "print ss" " = a=<a=<1> b=<$hex>> b=<a=<2> b=<$hex>>" \
     "print ss re-enabled"
+
+gdb_test "print (enum flag_enum) (FLAG_1)" \
+    " = 0x1 .FLAG_1." \
+    "print FLAG_1"
+
+gdb_test "print (enum flag_enum) (FLAG_1 | FLAG_3)" \
+    " = 0x5 .FLAG_1 | FLAG_3." \
+    "print FLAG_1 | FLAG_3"
+
+gdb_test "print (enum flag_enum) (4 + 8)" \
+    " = 0xc .FLAG_1 | <unknown: 0x8>." \
+    "print FLAG_1 | 8"
+
+
+
diff --git a/gdb/testsuite/gdb.python/py-pp-maint.py b/gdb/testsuite/gdb.python/py-pp-maint.py
index c35d1dc..3df11ea 100644
--- a/gdb/testsuite/gdb.python/py-pp-maint.py
+++ b/gdb/testsuite/gdb.python/py-pp-maint.py
@@ -67,6 +67,9 @@ def build_pretty_printer():
     pp.add_printer('struct ss', '^struct ss$', lambda val: pp_ss(val))
     pp.add_printer('ss', '^ss$', lambda val: pp_ss(val))
 
+    pp.add_printer('enum flag_enum', '^flag_enum$',
+                   gdb.printing.EnumerationPrinter('enum flag_enum'))
+
     return pp
 
 

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: RFC: enum pretty-printing support
  2012-01-10 20:26 RFC: enum pretty-printing support Tom Tromey
@ 2012-01-10 20:28 ` Pedro Alves
  2012-01-11 16:45   ` Tom Tromey
  2012-01-10 21:19 ` Eli Zaretskii
  2012-01-10 21:23 ` Jan Kratochvil
  2 siblings, 1 reply; 13+ messages in thread
From: Pedro Alves @ 2012-01-10 20:28 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On 01/10/2012 07:54 PM, Tom Tromey wrote:
> I'd appreciate comments on this patch.
>
> This adds a new helper class to gdb.printing to handle printing of enum
> values which are or'd together.  E.g.:
>
> print (enum flag_enum) (FLAG_1 | FLAG_3)
> $8 = 0x5 [FLAG_1 | FLAG_3]

I think this is great.

On 01/10/2012 07:54 PM, Tom Tromey wrote:
> +@item EnumerationPrinter (@var{name})
> +A pretty-printer which handles printing of @code{enum} values.  Unlike
> +@value{GDBN}'s built-in @code{enum} printing, this printer handles the
> +case where enumerators are or'd together to create a value.
> +@var{name} is the name of the printer and also the name of the
> +@code{enum} type to look up.

Is the intention to have this printer do more than work
with flag enums?  Otherwise, I'd suggest naming it FlagEnumerationPrinter
(or some such).

-- 
Pedro Alves

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: RFC: enum pretty-printing support
  2012-01-10 20:26 RFC: enum pretty-printing support Tom Tromey
  2012-01-10 20:28 ` Pedro Alves
@ 2012-01-10 21:19 ` Eli Zaretskii
  2012-01-10 21:23 ` Jan Kratochvil
  2 siblings, 0 replies; 13+ messages in thread
From: Eli Zaretskii @ 2012-01-10 21:19 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

> From: Tom Tromey <tromey@redhat.com>
> Date: Tue, 10 Jan 2012 12:54:39 -0700
> 
> b/gdb/doc/ChangeLog:
> 2012-01-10  Tom Tromey  <tromey@redhat.com>
> 
> 	* gdb.texinfo (gdb.printing): Document EnumerationPrinter.

This part is fine, thanks.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: RFC: enum pretty-printing support
  2012-01-10 20:26 RFC: enum pretty-printing support Tom Tromey
  2012-01-10 20:28 ` Pedro Alves
  2012-01-10 21:19 ` Eli Zaretskii
@ 2012-01-10 21:23 ` Jan Kratochvil
  2012-01-10 22:00   ` Tom Tromey
  2 siblings, 1 reply; 13+ messages in thread
From: Jan Kratochvil @ 2012-01-10 21:23 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On Tue, 10 Jan 2012 20:54:39 +0100, Tom Tromey wrote:
> I'd appreciate comments on this patch.

There is the obvious question this may work automatically if all the values of
enum type are in the form 2^n and fall back to the GDB default way otherwise.
Also it would not display parentheses if the variable value is just 2^n.

GDB could pass the type as `enum NAME' instead of just `NAME' to the pretty
printer regex.


Thanks,
Jan

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: RFC: enum pretty-printing support
  2012-01-10 21:23 ` Jan Kratochvil
@ 2012-01-10 22:00   ` Tom Tromey
  2012-01-10 22:19     ` Tom Tromey
  0 siblings, 1 reply; 13+ messages in thread
From: Tom Tromey @ 2012-01-10 22:00 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches

>>>>> "Jan" == Jan Kratochvil <jan.kratochvil@redhat.com> writes:

Jan> On Tue, 10 Jan 2012 20:54:39 +0100, Tom Tromey wrote:
>> I'd appreciate comments on this patch.

Jan> There is the obvious question this may work automatically if all
Jan> the values of enum type are in the form 2^n and fall back to the
Jan> GDB default way otherwise.

Yeah, that might be nicer.

I will look into it.

Jan> GDB could pass the type as `enum NAME' instead of just `NAME' to
Jan> the pretty printer regex.

I thought perhaps the user would want to register it against a typedef
as well.

Tom

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: RFC: enum pretty-printing support
  2012-01-10 22:00   ` Tom Tromey
@ 2012-01-10 22:19     ` Tom Tromey
  2012-01-10 23:52       ` Jan Kratochvil
  0 siblings, 1 reply; 13+ messages in thread
From: Tom Tromey @ 2012-01-10 22:19 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches

Jan> There is the obvious question this may work automatically if all
Jan> the values of enum type are in the form 2^n and fall back to the
Jan> GDB default way otherwise.

Tom> Yeah, that might be nicer.
Tom> I will look into it.

I think the basic code is pretty easy to write.
I have it written, but I can't test it until tomorrow.

I do wonder whether we still want the Python code.

My C implementation works by checking whether the enum values are
disjoint.  However, it is not uncommon to see things like:

 enum flags {
   ONE = 1,
   TWO = 2,
   HIGHEST = TWO
  };

or

 enum flags {
   ONE = 1,
   TWO = 2,
   ALL = ONE | TWO
  };

... but the Python code would acceptably handle both of these.  This was
ok for the Python approach because registration is explicit, not
implicit; I think the C code has to be a bit more careful because it
cannot readily be turned off.

One idea is to have both.

Tom

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: RFC: enum pretty-printing support
  2012-01-10 22:19     ` Tom Tromey
@ 2012-01-10 23:52       ` Jan Kratochvil
  2012-01-11 16:46         ` Tom Tromey
  0 siblings, 1 reply; 13+ messages in thread
From: Jan Kratochvil @ 2012-01-10 23:52 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On Tue, 10 Jan 2012 23:09:06 +0100, Tom Tromey wrote:
> I think the C code has to be a bit more careful because it
> cannot readily be turned off.
> 
> One idea is to have both.

I agree.


Thanks,
Jan

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: RFC: enum pretty-printing support
  2012-01-10 20:28 ` Pedro Alves
@ 2012-01-11 16:45   ` Tom Tromey
  0 siblings, 0 replies; 13+ messages in thread
From: Tom Tromey @ 2012-01-11 16:45 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

>>>>> "Pedro" == Pedro Alves <alves.ped@gmail.com> writes:

Pedro> Is the intention to have this printer do more than work
Pedro> with flag enums?  Otherwise, I'd suggest naming it FlagEnumerationPrinter
Pedro> (or some such).

I made this change.
Thanks.

Tom

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: RFC: enum pretty-printing support
  2012-01-10 23:52       ` Jan Kratochvil
@ 2012-01-11 16:46         ` Tom Tromey
  2012-01-11 21:03           ` Jan Kratochvil
  0 siblings, 1 reply; 13+ messages in thread
From: Tom Tromey @ 2012-01-11 16:46 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches

Tom> I think the C code has to be a bit more careful because it
Tom> cannot readily be turned off.
Tom> 
Tom> One idea is to have both.

Jan> I agree.

Here's a new patch.  Let me know what you think.

This adds basic flag enum printing to c-valprint.c.  I looked at other
languages, but I don't know their syntax enough to say whether this same
approach is ok.  Refactoring a bit to allow this later is trivial, if
need be.

Looking into this caught a bug in the original code.  The "leftover
value" code has to handle the case where the input value was 0.
Otherwise, gdb will print nothing :)

I only updated the DWARF reader since I know nothing about stabs; and,
anyway, this is just a nicety.

This version needs another doc review.  I also added NEWS entries.

Built and regtested on x86-64 F15.

Tom

b/gdb/ChangeLog:
2012-01-11  Tom Tromey  <tromey@redhat.com>

	PR python/13281:
	* gdbtypes.h (TYPE_FLAG_ENUM): New macro.
	(struct main_type) <flag_flag_enum>: New field.
	* dwarf2read.c (process_enumeration_scope): Detect "flag" enums.
	* NEWS: Add entries.
	* c-valprint.c (c_val_print) <TYPE_CODE_ENUM>: Handle "flag"
	enums.
	* python/lib/gdb/printing.py (_EnumInstance): New class.
	(FlagEnumerationPrinter): Likewise.

b/gdb/doc/ChangeLog:
2012-01-11  Tom Tromey  <tromey@redhat.com>

	* gdb.texinfo (gdb.printing): Document FlagEnumerationPrinter.

b/gdb/testsuite/ChangeLog:
2012-01-11  Tom Tromey  <tromey@redhat.com>

	* gdb.base/printcmds.c (enum flag_enum): New.
	(three): New global.
	* gdb.base/printcmds.exp (test_print_enums): Add test for flag
	enum printing.
	* gdb.python/py-pp-maint.py (build_pretty_printer): Instantiate
	FlagEnumerationPrinter.
	* gdb.python/py-pp-maint.exp: Add tests for FlagEnumerationPrinter.
	* gdb.python/py-pp-maint.c (enum flag_enum): New.
	(fval): New global.

diff --git a/gdb/NEWS b/gdb/NEWS
index a9a7859..8119cf0 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -9,6 +9,18 @@
 * The binary "gdbtui" can no longer be built or installed.
   Use "gdb -tui" instead.
 
+* GDB will now print "flag" enums specially.  A flag enum is one where
+  all the enumerator values have no bits in common when pairwise
+  "and"ed.  When printing a value whose type is a flag enum, GDB will
+  show all the constants, e.g., for enum E { ONE = 1, TWO = 2}:
+  (gdb) print (enum E) 3
+  $1 = [ONE | TWO]
+
+* Python scripting
+
+  ** A new class, gdb.printing.FlagEnumerationPrinter, can be used to
+     apply "flag enum"-style pretty-printing to any enum.
+
 *** Changes in GDB 7.4
 
 * GDB now handles ambiguous linespecs more consistently; the existing
diff --git a/gdb/c-valprint.c b/gdb/c-valprint.c
index 9949015..1d44e90 100644
--- a/gdb/c-valprint.c
+++ b/gdb/c-valprint.c
@@ -458,7 +458,38 @@ c_val_print (struct type *type, const gdb_byte *valaddr,
 	}
       else
 	{
-	  print_longest (stream, 'd', 0, val);
+	  if (TYPE_FLAG_ENUM (type))
+	    {
+	      int first = 1;
+
+	      fputs_filtered ("[", stream);
+	      for (i = 0; i < len; ++i)
+		{
+		  QUIT;
+
+		  if ((val & TYPE_FIELD_BITPOS (type, i)) != 0)
+		    {
+		      if (!first)
+			fputs_filtered (" | ", stream);
+		      first = 0;
+
+		      val &= ~ TYPE_FIELD_BITPOS (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);
+		}
+
+	      fputs_filtered ("]", stream);
+	    }
+	  else
+	    print_longest (stream, 'd', 0, val);
 	}
       break;
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 4a8ff7b..de2e390 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -24720,6 +24720,13 @@ Utility class for handling multiple printers, all recognized via
 regular expressions.
 @xref{Writing a Pretty-Printer}, for an example.
 
+@item FlagEnumerationPrinter (@var{name})
+A pretty-printer which handles printing of @code{enum} values.  Unlike
+@value{GDBN}'s built-in @code{enum} printing, this printer attempts to
+work properly when there is some overlap between the enumeration
+constants.  @var{name} is the name of the printer and also the name of
+the @code{enum} type to look up.
+
 @item register_pretty_printer (@var{obj}, @var{printer}, @var{replace}=False)
 Register @var{printer} with the pretty-printer list of @var{obj}.
 If @var{replace} is @code{True} then any existing copy of the printer
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 42dbac3..afb4337 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -7913,6 +7913,8 @@ process_enumeration_scope (struct die_info *die, struct dwarf2_cu *cu)
       int num_fields = 0;
       int unsigned_enum = 1;
       char *name;
+      int flag_enum = 1;
+      ULONGEST mask = 0;
 
       child_die = die->child;
       while (child_die && child_die->tag)
@@ -7928,7 +7930,14 @@ process_enumeration_scope (struct die_info *die, struct dwarf2_cu *cu)
 		{
 		  sym = new_symbol (child_die, this_type, cu);
 		  if (SYMBOL_VALUE (sym) < 0)
-		    unsigned_enum = 0;
+		    {
+		      unsigned_enum = 0;
+		      flag_enum = 0;
+		    }
+		  else if ((mask & SYMBOL_VALUE (sym)) != 0)
+		    flag_enum = 0;
+		  else
+		    mask |= SYMBOL_VALUE (sym);
 
 		  if ((num_fields % DW_FIELD_ALLOC_CHUNK) == 0)
 		    {
@@ -7961,6 +7970,8 @@ process_enumeration_scope (struct die_info *die, struct dwarf2_cu *cu)
 	}
       if (unsigned_enum)
 	TYPE_UNSIGNED (this_type) = 1;
+      if (flag_enum)
+	TYPE_FLAG_ENUM (this_type) = 1;
     }
 
   /* If we are reading an enum from a .debug_types unit, and the enum
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index ddad0dc..f152945 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -290,6 +290,12 @@ enum type_instance_flag_value
 
 #define TYPE_DECLARED_CLASS(t) (TYPE_MAIN_TYPE (t)->flag_declared_class)
 
+/* True if this type is a "flag" enum.  A flag enum is one where all
+   the values are pairwise disjoint when "and"ed together.  This
+   affects how enum values are printed.  */
+
+#define TYPE_FLAG_ENUM(t) (TYPE_MAIN_TYPE (t)->flag_flag_enum)
+
 /* Constant type.  If this is set, the corresponding type has a
    const modifier.  */
 
@@ -399,6 +405,9 @@ struct main_type
   /* True if this type was declared with "class" rather than
      "struct".  */
   unsigned int flag_declared_class : 1;
+  /* True if this is an enum type with disjoint values.  This affects
+     how the enum is printed.  */
+  unsigned int flag_flag_enum : 1;
 
   /* A discriminant telling us which field of the type_specific union
      is being used for this type, if any.  */
diff --git a/gdb/python/lib/gdb/printing.py b/gdb/python/lib/gdb/printing.py
index 98cfd27..0f399d0 100644
--- a/gdb/python/lib/gdb/printing.py
+++ b/gdb/python/lib/gdb/printing.py
@@ -206,3 +206,53 @@ class RegexpCollectionPrettyPrinter(PrettyPrinter):
 
         # Cannot find a pretty printer.  Return None.
         return None
+
+# A helper class for printing enum types.  This class is instantiated
+# with a list of enumerators to print a particular Value.
+class _EnumInstance:
+    def __init__(self, enumerators, val):
+        self.enumerators = enumerators
+        self.val = val
+
+    def to_string(self):
+        flag_list = []
+        v = long(self.val)
+        any_found = False
+        for (e_name, e_value) in self.enumerators:
+            if v & e_value != 0:
+                flag_list.append(e_name)
+                v = v & ~e_value
+                any_found = True
+        if not any_found or v != 0:
+            # Leftover value.
+            flag_list.append('<unknown: 0x%x>' % v)
+        return "0x%x [%s]" % (self.val, " | ".join(flag_list))
+
+class FlagEnumerationPrinter(PrettyPrinter):
+    """A pretty-printer which can be used to print a flag-style enumeration.
+    A flag-style enumeration is one where the enumerators are or'd
+    together to create values.  The new printer will print these
+    symbolically using '|' notation.  The printer must be registered
+    manually.  This printer is most useful when an enum is flag-like,
+    but has some overlap.  GDB's built-in printing will not handle
+    this case, but this printer will attempt to."""
+
+    def __init__(self, enum_type):
+        super(FlagEnumerationPrinter, self).__init__(enum_type)
+        self.initialized = False
+
+    def __call__(self, val):
+        if not self.initialized:
+            self.initialized = True
+            flags = gdb.lookup_type(self.name)
+            self.enumerators = []
+            for field in flags.fields():
+                self.enumerators.append((field.name, field.bitpos))
+            # Sorting the enumerators by value usually does the right
+            # thing.
+            self.enumerators.sort(key = lambda x: x.bitpos)
+
+        if self.enabled:
+            return _EnumInstance(self.enumerators, val)
+        else:
+            return None
diff --git a/gdb/testsuite/gdb.base/printcmds.c b/gdb/testsuite/gdb.base/printcmds.c
index d37dfbd..743734b 100644
--- a/gdb/testsuite/gdb.base/printcmds.c
+++ b/gdb/testsuite/gdb.base/printcmds.c
@@ -96,6 +96,10 @@ enum some_volatile_enum { enumvolval1, enumvolval2 };
    name.  See PR11827.  */
 volatile enum some_volatile_enum some_volatile_enum = enumvolval1;
 
+enum flag_enum { ONE = 1, TWO = 2 };
+
+enum flag_enum three = ONE | TWO;
+
 /* A structure with an embedded array at an offset > 0.  The array has
    all elements with the same repeating value, which must not be the
    same as the value of the preceding fields in the structure for the
diff --git a/gdb/testsuite/gdb.base/printcmds.exp b/gdb/testsuite/gdb.base/printcmds.exp
index d647ca8..36d3af6 100644
--- a/gdb/testsuite/gdb.base/printcmds.exp
+++ b/gdb/testsuite/gdb.base/printcmds.exp
@@ -701,6 +701,8 @@ proc test_print_array_constants {} {
 proc test_print_enums {} {
     # Regression test for PR11827.
     gdb_test "print some_volatile_enum" "enumvolval1"
+
+    gdb_test "print three" " = \\\[ONE \\| TWO\\\]"
 }
 
 proc test_printf {} {
diff --git a/gdb/testsuite/gdb.python/py-pp-maint.c b/gdb/testsuite/gdb.python/py-pp-maint.c
index f65e5f7..e91193a 100644
--- a/gdb/testsuite/gdb.python/py-pp-maint.c
+++ b/gdb/testsuite/gdb.python/py-pp-maint.c
@@ -17,6 +17,16 @@
 
 #include <string.h>
 
+enum flag_enum
+  {
+    FLAG_1 = 1,
+    FLAG_2 = 2,
+    FLAG_3 = 4,
+    ALL = FLAG_1 | FLAG_2 | FLAG_3
+  };
+
+enum flag_enum fval;
+
 struct function_lookup_test
 {
   int x,y;
diff --git a/gdb/testsuite/gdb.python/py-pp-maint.exp b/gdb/testsuite/gdb.python/py-pp-maint.exp
index 1a2b213..5f591fb 100644
--- a/gdb/testsuite/gdb.python/py-pp-maint.exp
+++ b/gdb/testsuite/gdb.python/py-pp-maint.exp
@@ -74,23 +74,25 @@ gdb_test "print flt" " = x=<42> y=<43>" \
 gdb_test "print ss" " = a=<a=<1> b=<$hex>> b=<a=<2> b=<$hex>>" \
     "print ss enabled #1"
 
+set num_pp 6
+
 gdb_test "disable pretty-printer" \
-    "5 printers disabled.*0 of 5 printers enabled"
+    "$num_pp printers disabled.*0 of $num_pp printers enabled"
 
 gdb_test "enable pretty-printer" \
-    "5 printers enabled.*5 of 5 printers enabled"
+    "$num_pp printers enabled.*$num_pp of $num_pp printers enabled"
 
 gdb_test "disable pretty-printer global" \
-    "5 printers disabled.*0 of 5 printers enabled"
+    "$num_pp printers disabled.*0 of $num_pp printers enabled"
 
 gdb_test "enable pretty-printer" \
-    "5 printers enabled.*5 of 5 printers enabled"
+    "$num_pp printers enabled.*$num_pp of $num_pp printers enabled"
 
 gdb_test "disable pretty-printer global lookup_function_lookup_test" \
-    "1 printer disabled.*4 of 5 printers enabled"
+    "1 printer disabled.*[expr $num_pp - 1] of $num_pp printers enabled"
 
 gdb_test "disable pretty-printer global pp-test;.*" \
-    "4 printers disabled.*0 of 5 printers enabled"
+    "[expr $num_pp - 1] printers disabled.*0 of $num_pp printers enabled"
 
 gdb_test "info pretty-printer global .*function" \
     {.*function_lookup_test \[disabled\].*}
@@ -105,19 +107,22 @@ gdb_test "print ss" " = {a = {a = 1, b = $hex}, b = {a = 2, b = $hex}}" \
     "print ss disabled"
 
 gdb_test "enable pretty-printer global lookup_function_lookup_test" \
-    "1 printer enabled.*1 of 5 printers enabled"
+    "1 printer enabled.*1 of $num_pp printers enabled"
 
 # This doesn't enable any printers because each subprinter in the collection
 # is still individually disabled.  But this is still needed, to enable the
 # collection itself.
 gdb_test "enable pretty-printer global pp-test" \
-    "0 printers enabled.*1 of 5 printers enabled"
+    "0 printers enabled.*1 of $num_pp printers enabled"
 
 gdb_test "enable pretty-printer global pp-test;.*ss.*" \
-    "2 printers enabled.*3 of 5 printers enabled"
+    "2 printers enabled.*[expr $num_pp - 3] of $num_pp printers enabled"
 
 gdb_test "enable pretty-printer global pp-test;.*s.*" \
-    "2 printers enabled.*5 of 5 printers enabled"
+    "2 printers enabled.*[expr $num_pp - 1] of $num_pp printers enabled"
+
+gdb_test "enable pretty-printer global pp-test;.*" \
+    "1 printer enabled.*$num_pp of $num_pp printers enabled"
 
 gdb_test "info pretty-printer" \
     {.*function_lookup_test.*pp-test.*struct ss.*}
@@ -127,3 +132,18 @@ gdb_test "print flt" " = x=<42> y=<43>" \
 
 gdb_test "print ss" " = a=<a=<1> b=<$hex>> b=<a=<2> b=<$hex>>" \
     "print ss re-enabled"
+
+gdb_test "print (enum flag_enum) (FLAG_1)" \
+    " = 0x1 .FLAG_1." \
+    "print FLAG_1"
+
+gdb_test "print (enum flag_enum) (FLAG_1 | FLAG_3)" \
+    " = 0x5 .FLAG_1 | FLAG_3." \
+    "print FLAG_1 | FLAG_3"
+
+gdb_test "print (enum flag_enum) (4 + 8)" \
+    " = 0xc .FLAG_1 | <unknown: 0x8>." \
+    "print FLAG_1 | 8"
+
+
+
diff --git a/gdb/testsuite/gdb.python/py-pp-maint.py b/gdb/testsuite/gdb.python/py-pp-maint.py
index c988f88..1677371 100644
--- a/gdb/testsuite/gdb.python/py-pp-maint.py
+++ b/gdb/testsuite/gdb.python/py-pp-maint.py
@@ -67,6 +67,9 @@ def build_pretty_printer():
     pp.add_printer('struct ss', '^struct ss$', lambda val: pp_ss(val))
     pp.add_printer('ss', '^ss$', lambda val: pp_ss(val))
 
+    pp.add_printer('enum flag_enum', '^flag_enum$',
+                   gdb.printing.FlagEnumerationPrinter('enum flag_enum'))
+
     return pp
 
 

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: RFC: enum pretty-printing support
  2012-01-11 16:46         ` Tom Tromey
@ 2012-01-11 21:03           ` Jan Kratochvil
  2012-01-11 21:25             ` Tom Tromey
  0 siblings, 1 reply; 13+ messages in thread
From: Jan Kratochvil @ 2012-01-11 21:03 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

Hi Tom,

On Wed, 11 Jan 2012 17:45:16 +0100, Tom Tromey wrote:
> Tom> I think the C code has to be a bit more careful because it
> Tom> cannot readily be turned off.
> Tom> 
> Tom> One idea is to have both.
> 
> Jan> I agree.
> 
> Here's a new patch.  Let me know what you think.

That's great now, thanks.  FYI not reviewed the Python part.


> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -9,6 +9,18 @@
>  * The binary "gdbtui" can no longer be built or installed.
>    Use "gdb -tui" instead.
>  
> +* GDB will now print "flag" enums specially.  A flag enum is one where
> +  all the enumerator values have no bits in common when pairwise
> +  "and"ed.  When printing a value whose type is a flag enum, GDB will
> +  show all the constants, e.g., for enum E { ONE = 1, TWO = 2}:
> +  (gdb) print (enum E) 3
> +  $1 = [ONE | TWO]

I do not much understand - why not to make it C syntax compliant?
     $1 = (ONE | TWO)


> +
> +* Python scripting
> +
> +  ** A new class, gdb.printing.FlagEnumerationPrinter, can be used to
> +     apply "flag enum"-style pretty-printing to any enum.
> +
>  *** Changes in GDB 7.4
>  
>  * GDB now handles ambiguous linespecs more consistently; the existing
> diff --git a/gdb/c-valprint.c b/gdb/c-valprint.c
> index 9949015..1d44e90 100644
> --- a/gdb/c-valprint.c
> +++ b/gdb/c-valprint.c
> @@ -458,7 +458,38 @@ c_val_print (struct type *type, const gdb_byte *valaddr,
>  	}
>        else
>  	{
> -	  print_longest (stream, 'd', 0, val);
> +	  if (TYPE_FLAG_ENUM (type))

Some comment here what this new block does, please.

This "else if" can be indented left - one needless block/indentation here.


> +	    {
> +	      int first = 1;
> +
> +	      fputs_filtered ("[", stream);
> +	      for (i = 0; i < len; ++i)
> +		{
> +		  QUIT;
> +
> +		  if ((val & TYPE_FIELD_BITPOS (type, i)) != 0)
> +		    {
> +		      if (!first)
> +			fputs_filtered (" | ", stream);
> +		      first = 0;
> +
> +		      val &= ~ TYPE_FIELD_BITPOS (type, i);
                              ^
Extra space, not GDB coding style compliant.


> +		      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);
> +		}
> +
> +	      fputs_filtered ("]", stream);
> +	    }
> +	  else
> +	    print_longest (stream, 'd', 0, val);
>  	}
>        break;
>  
[...]
> diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
> index ddad0dc..f152945 100644
> --- a/gdb/gdbtypes.h
> +++ b/gdb/gdbtypes.h
> @@ -290,6 +290,12 @@ enum type_instance_flag_value
>  
>  #define TYPE_DECLARED_CLASS(t) (TYPE_MAIN_TYPE (t)->flag_declared_class)
>  
> +/* True if this type is a "flag" enum.  A flag enum is one where all
> +   the values are pairwise disjoint when "and"ed together.  This
> +   affects how enum values are printed.  */
> +
> +#define TYPE_FLAG_ENUM(t) (TYPE_MAIN_TYPE (t)->flag_flag_enum)
> +
>  /* Constant type.  If this is set, the corresponding type has a
>     const modifier.  */
>  
> @@ -399,6 +405,9 @@ struct main_type
>    /* True if this type was declared with "class" rather than
>       "struct".  */
>    unsigned int flag_declared_class : 1;

Empty line (OK, flag_declared_class is also wrong wrt this).

> +  /* True if this is an enum type with disjoint values.  This affects
> +     how the enum is printed.  */
> +  unsigned int flag_flag_enum : 1;
>  
>    /* A discriminant telling us which field of the type_specific union
>       is being used for this type, if any.  */
[...]
> --- a/gdb/testsuite/gdb.python/py-pp-maint.exp
> +++ b/gdb/testsuite/gdb.python/py-pp-maint.exp
[...]
> @@ -127,3 +132,18 @@ gdb_test "print flt" " = x=<42> y=<43>" \
>  
>  gdb_test "print ss" " = a=<a=<1> b=<$hex>> b=<a=<2> b=<$hex>>" \
>      "print ss re-enabled"
> +
> +gdb_test "print (enum flag_enum) (FLAG_1)" \
> +    " = 0x1 .FLAG_1." \
> +    "print FLAG_1"
> +
> +gdb_test "print (enum flag_enum) (FLAG_1 | FLAG_3)" \
> +    " = 0x5 .FLAG_1 | FLAG_3." \
> +    "print FLAG_1 | FLAG_3"
> +
> +gdb_test "print (enum flag_enum) (4 + 8)" \
> +    " = 0xc .FLAG_1 | <unknown: 0x8>." \
> +    "print FLAG_1 | 8"
> +
> +
> +

3 extra empty lines.



Thanks,
Jan

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: RFC: enum pretty-printing support
  2012-01-11 21:03           ` Jan Kratochvil
@ 2012-01-11 21:25             ` Tom Tromey
  2012-01-16 18:44               ` Tom Tromey
  0 siblings, 1 reply; 13+ messages in thread
From: Tom Tromey @ 2012-01-11 21:25 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches

Jan> That's great now, thanks.  FYI not reviewed the Python part.

Thanks.

Jan> I do not much understand - why not to make it C syntax compliant?
Jan>      $1 = (ONE | TWO)

No good reason, just took it from the Python code.

This version should address all your comments.

Tom

b/gdb/ChangeLog:
2012-01-11  Tom Tromey  <tromey@redhat.com>

	PR python/13281:
	* gdbtypes.h (TYPE_FLAG_ENUM): New macro.
	(struct main_type) <flag_flag_enum>: New field.
	* dwarf2read.c (process_enumeration_scope): Detect "flag" enums.
	* NEWS: Add entries.
	* c-valprint.c (c_val_print) <TYPE_CODE_ENUM>: Handle "flag"
	enums.
	* python/lib/gdb/printing.py (_EnumInstance): New class.
	(FlagEnumerationPrinter): Likewise.

b/gdb/doc/ChangeLog:
2012-01-11  Tom Tromey  <tromey@redhat.com>

	* gdb.texinfo (gdb.printing): Document FlagEnumerationPrinter.

b/gdb/testsuite/ChangeLog:
2012-01-11  Tom Tromey  <tromey@redhat.com>

	* gdb.base/printcmds.c (enum flag_enum): New.
	(three): New global.
	* gdb.base/printcmds.exp (test_print_enums): Add test for flag
	enum printing.
	* gdb.python/py-pp-maint.py (build_pretty_printer): Instantiate
	FlagEnumerationPrinter.
	* gdb.python/py-pp-maint.exp: Add tests for FlagEnumerationPrinter.
	* gdb.python/py-pp-maint.c (enum flag_enum): New.
	(fval): New global.

diff --git a/gdb/NEWS b/gdb/NEWS
index a9a7859..571fd5e 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -9,6 +9,18 @@
 * The binary "gdbtui" can no longer be built or installed.
   Use "gdb -tui" instead.
 
+* GDB will now print "flag" enums specially.  A flag enum is one where
+  all the enumerator values have no bits in common when pairwise
+  "and"ed.  When printing a value whose type is a flag enum, GDB will
+  show all the constants, e.g., for enum E { ONE = 1, TWO = 2}:
+  (gdb) print (enum E) 3
+  $1 = (ONE | TWO)
+
+* Python scripting
+
+  ** A new class, gdb.printing.FlagEnumerationPrinter, can be used to
+     apply "flag enum"-style pretty-printing to any enum.
+
 *** Changes in GDB 7.4
 
 * GDB now handles ambiguous linespecs more consistently; the existing
diff --git a/gdb/c-valprint.c b/gdb/c-valprint.c
index 9949015..82551e9 100644
--- a/gdb/c-valprint.c
+++ b/gdb/c-valprint.c
@@ -456,10 +456,41 @@ c_val_print (struct type *type, const gdb_byte *valaddr,
 	{
 	  fputs_filtered (TYPE_FIELD_NAME (type, i), stream);
 	}
-      else
+      else if (TYPE_FLAG_ENUM (type))
 	{
-	  print_longest (stream, 'd', 0, val);
+	  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.  */
+	  fputs_filtered ("(", stream);
+	  for (i = 0; i < len; ++i)
+	    {
+	      QUIT;
+
+	      if ((val & TYPE_FIELD_BITPOS (type, i)) != 0)
+		{
+		  if (!first)
+		    fputs_filtered (" | ", stream);
+		  first = 0;
+
+		  val &= ~TYPE_FIELD_BITPOS (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);
+	    }
+
+	  fputs_filtered (")", stream);
 	}
+      else
+	print_longest (stream, 'd', 0, val);
       break;
 
     case TYPE_CODE_FLAGS:
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 4a8ff7b..de2e390 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -24720,6 +24720,13 @@ Utility class for handling multiple printers, all recognized via
 regular expressions.
 @xref{Writing a Pretty-Printer}, for an example.
 
+@item FlagEnumerationPrinter (@var{name})
+A pretty-printer which handles printing of @code{enum} values.  Unlike
+@value{GDBN}'s built-in @code{enum} printing, this printer attempts to
+work properly when there is some overlap between the enumeration
+constants.  @var{name} is the name of the printer and also the name of
+the @code{enum} type to look up.
+
 @item register_pretty_printer (@var{obj}, @var{printer}, @var{replace}=False)
 Register @var{printer} with the pretty-printer list of @var{obj}.
 If @var{replace} is @code{True} then any existing copy of the printer
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 42dbac3..afb4337 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -7913,6 +7913,8 @@ process_enumeration_scope (struct die_info *die, struct dwarf2_cu *cu)
       int num_fields = 0;
       int unsigned_enum = 1;
       char *name;
+      int flag_enum = 1;
+      ULONGEST mask = 0;
 
       child_die = die->child;
       while (child_die && child_die->tag)
@@ -7928,7 +7930,14 @@ process_enumeration_scope (struct die_info *die, struct dwarf2_cu *cu)
 		{
 		  sym = new_symbol (child_die, this_type, cu);
 		  if (SYMBOL_VALUE (sym) < 0)
-		    unsigned_enum = 0;
+		    {
+		      unsigned_enum = 0;
+		      flag_enum = 0;
+		    }
+		  else if ((mask & SYMBOL_VALUE (sym)) != 0)
+		    flag_enum = 0;
+		  else
+		    mask |= SYMBOL_VALUE (sym);
 
 		  if ((num_fields % DW_FIELD_ALLOC_CHUNK) == 0)
 		    {
@@ -7961,6 +7970,8 @@ process_enumeration_scope (struct die_info *die, struct dwarf2_cu *cu)
 	}
       if (unsigned_enum)
 	TYPE_UNSIGNED (this_type) = 1;
+      if (flag_enum)
+	TYPE_FLAG_ENUM (this_type) = 1;
     }
 
   /* If we are reading an enum from a .debug_types unit, and the enum
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index ddad0dc..2070f00 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -290,6 +290,12 @@ enum type_instance_flag_value
 
 #define TYPE_DECLARED_CLASS(t) (TYPE_MAIN_TYPE (t)->flag_declared_class)
 
+/* True if this type is a "flag" enum.  A flag enum is one where all
+   the values are pairwise disjoint when "and"ed together.  This
+   affects how enum values are printed.  */
+
+#define TYPE_FLAG_ENUM(t) (TYPE_MAIN_TYPE (t)->flag_flag_enum)
+
 /* Constant type.  If this is set, the corresponding type has a
    const modifier.  */
 
@@ -400,6 +406,11 @@ struct main_type
      "struct".  */
   unsigned int flag_declared_class : 1;
 
+  /* True if this is an enum type with disjoint values.  This affects
+     how the enum is printed.  */
+
+  unsigned int flag_flag_enum : 1;
+
   /* A discriminant telling us which field of the type_specific union
      is being used for this type, if any.  */
   ENUM_BITFIELD(type_specific_kind) type_specific_field : 3;
diff --git a/gdb/python/lib/gdb/printing.py b/gdb/python/lib/gdb/printing.py
index 98cfd27..0f399d0 100644
--- a/gdb/python/lib/gdb/printing.py
+++ b/gdb/python/lib/gdb/printing.py
@@ -206,3 +206,53 @@ class RegexpCollectionPrettyPrinter(PrettyPrinter):
 
         # Cannot find a pretty printer.  Return None.
         return None
+
+# A helper class for printing enum types.  This class is instantiated
+# with a list of enumerators to print a particular Value.
+class _EnumInstance:
+    def __init__(self, enumerators, val):
+        self.enumerators = enumerators
+        self.val = val
+
+    def to_string(self):
+        flag_list = []
+        v = long(self.val)
+        any_found = False
+        for (e_name, e_value) in self.enumerators:
+            if v & e_value != 0:
+                flag_list.append(e_name)
+                v = v & ~e_value
+                any_found = True
+        if not any_found or v != 0:
+            # Leftover value.
+            flag_list.append('<unknown: 0x%x>' % v)
+        return "0x%x [%s]" % (self.val, " | ".join(flag_list))
+
+class FlagEnumerationPrinter(PrettyPrinter):
+    """A pretty-printer which can be used to print a flag-style enumeration.
+    A flag-style enumeration is one where the enumerators are or'd
+    together to create values.  The new printer will print these
+    symbolically using '|' notation.  The printer must be registered
+    manually.  This printer is most useful when an enum is flag-like,
+    but has some overlap.  GDB's built-in printing will not handle
+    this case, but this printer will attempt to."""
+
+    def __init__(self, enum_type):
+        super(FlagEnumerationPrinter, self).__init__(enum_type)
+        self.initialized = False
+
+    def __call__(self, val):
+        if not self.initialized:
+            self.initialized = True
+            flags = gdb.lookup_type(self.name)
+            self.enumerators = []
+            for field in flags.fields():
+                self.enumerators.append((field.name, field.bitpos))
+            # Sorting the enumerators by value usually does the right
+            # thing.
+            self.enumerators.sort(key = lambda x: x.bitpos)
+
+        if self.enabled:
+            return _EnumInstance(self.enumerators, val)
+        else:
+            return None
diff --git a/gdb/testsuite/gdb.base/printcmds.c b/gdb/testsuite/gdb.base/printcmds.c
index d37dfbd..743734b 100644
--- a/gdb/testsuite/gdb.base/printcmds.c
+++ b/gdb/testsuite/gdb.base/printcmds.c
@@ -96,6 +96,10 @@ enum some_volatile_enum { enumvolval1, enumvolval2 };
    name.  See PR11827.  */
 volatile enum some_volatile_enum some_volatile_enum = enumvolval1;
 
+enum flag_enum { ONE = 1, TWO = 2 };
+
+enum flag_enum three = ONE | TWO;
+
 /* A structure with an embedded array at an offset > 0.  The array has
    all elements with the same repeating value, which must not be the
    same as the value of the preceding fields in the structure for the
diff --git a/gdb/testsuite/gdb.base/printcmds.exp b/gdb/testsuite/gdb.base/printcmds.exp
index d647ca8..a7fb4bd 100644
--- a/gdb/testsuite/gdb.base/printcmds.exp
+++ b/gdb/testsuite/gdb.base/printcmds.exp
@@ -701,6 +701,8 @@ proc test_print_array_constants {} {
 proc test_print_enums {} {
     # Regression test for PR11827.
     gdb_test "print some_volatile_enum" "enumvolval1"
+
+    gdb_test "print three" " = \\\(ONE \\| TWO\\\)"
 }
 
 proc test_printf {} {
diff --git a/gdb/testsuite/gdb.python/py-pp-maint.c b/gdb/testsuite/gdb.python/py-pp-maint.c
index f65e5f7..e91193a 100644
--- a/gdb/testsuite/gdb.python/py-pp-maint.c
+++ b/gdb/testsuite/gdb.python/py-pp-maint.c
@@ -17,6 +17,16 @@
 
 #include <string.h>
 
+enum flag_enum
+  {
+    FLAG_1 = 1,
+    FLAG_2 = 2,
+    FLAG_3 = 4,
+    ALL = FLAG_1 | FLAG_2 | FLAG_3
+  };
+
+enum flag_enum fval;
+
 struct function_lookup_test
 {
   int x,y;
diff --git a/gdb/testsuite/gdb.python/py-pp-maint.exp b/gdb/testsuite/gdb.python/py-pp-maint.exp
index 1a2b213..3d8e774 100644
--- a/gdb/testsuite/gdb.python/py-pp-maint.exp
+++ b/gdb/testsuite/gdb.python/py-pp-maint.exp
@@ -74,23 +74,25 @@ gdb_test "print flt" " = x=<42> y=<43>" \
 gdb_test "print ss" " = a=<a=<1> b=<$hex>> b=<a=<2> b=<$hex>>" \
     "print ss enabled #1"
 
+set num_pp 6
+
 gdb_test "disable pretty-printer" \
-    "5 printers disabled.*0 of 5 printers enabled"
+    "$num_pp printers disabled.*0 of $num_pp printers enabled"
 
 gdb_test "enable pretty-printer" \
-    "5 printers enabled.*5 of 5 printers enabled"
+    "$num_pp printers enabled.*$num_pp of $num_pp printers enabled"
 
 gdb_test "disable pretty-printer global" \
-    "5 printers disabled.*0 of 5 printers enabled"
+    "$num_pp printers disabled.*0 of $num_pp printers enabled"
 
 gdb_test "enable pretty-printer" \
-    "5 printers enabled.*5 of 5 printers enabled"
+    "$num_pp printers enabled.*$num_pp of $num_pp printers enabled"
 
 gdb_test "disable pretty-printer global lookup_function_lookup_test" \
-    "1 printer disabled.*4 of 5 printers enabled"
+    "1 printer disabled.*[expr $num_pp - 1] of $num_pp printers enabled"
 
 gdb_test "disable pretty-printer global pp-test;.*" \
-    "4 printers disabled.*0 of 5 printers enabled"
+    "[expr $num_pp - 1] printers disabled.*0 of $num_pp printers enabled"
 
 gdb_test "info pretty-printer global .*function" \
     {.*function_lookup_test \[disabled\].*}
@@ -105,19 +107,22 @@ gdb_test "print ss" " = {a = {a = 1, b = $hex}, b = {a = 2, b = $hex}}" \
     "print ss disabled"
 
 gdb_test "enable pretty-printer global lookup_function_lookup_test" \
-    "1 printer enabled.*1 of 5 printers enabled"
+    "1 printer enabled.*1 of $num_pp printers enabled"
 
 # This doesn't enable any printers because each subprinter in the collection
 # is still individually disabled.  But this is still needed, to enable the
 # collection itself.
 gdb_test "enable pretty-printer global pp-test" \
-    "0 printers enabled.*1 of 5 printers enabled"
+    "0 printers enabled.*1 of $num_pp printers enabled"
 
 gdb_test "enable pretty-printer global pp-test;.*ss.*" \
-    "2 printers enabled.*3 of 5 printers enabled"
+    "2 printers enabled.*[expr $num_pp - 3] of $num_pp printers enabled"
 
 gdb_test "enable pretty-printer global pp-test;.*s.*" \
-    "2 printers enabled.*5 of 5 printers enabled"
+    "2 printers enabled.*[expr $num_pp - 1] of $num_pp printers enabled"
+
+gdb_test "enable pretty-printer global pp-test;.*" \
+    "1 printer enabled.*$num_pp of $num_pp printers enabled"
 
 gdb_test "info pretty-printer" \
     {.*function_lookup_test.*pp-test.*struct ss.*}
@@ -127,3 +132,15 @@ gdb_test "print flt" " = x=<42> y=<43>" \
 
 gdb_test "print ss" " = a=<a=<1> b=<$hex>> b=<a=<2> b=<$hex>>" \
     "print ss re-enabled"
+
+gdb_test "print (enum flag_enum) (FLAG_1)" \
+    " = 0x1 .FLAG_1." \
+    "print FLAG_1"
+
+gdb_test "print (enum flag_enum) (FLAG_1 | FLAG_3)" \
+    " = 0x5 .FLAG_1 | FLAG_3." \
+    "print FLAG_1 | FLAG_3"
+
+gdb_test "print (enum flag_enum) (4 + 8)" \
+    " = 0xc .FLAG_1 | <unknown: 0x8>." \
+    "print FLAG_1 | 8"
diff --git a/gdb/testsuite/gdb.python/py-pp-maint.py b/gdb/testsuite/gdb.python/py-pp-maint.py
index c988f88..1677371 100644
--- a/gdb/testsuite/gdb.python/py-pp-maint.py
+++ b/gdb/testsuite/gdb.python/py-pp-maint.py
@@ -67,6 +67,9 @@ def build_pretty_printer():
     pp.add_printer('struct ss', '^struct ss$', lambda val: pp_ss(val))
     pp.add_printer('ss', '^ss$', lambda val: pp_ss(val))
 
+    pp.add_printer('enum flag_enum', '^flag_enum$',
+                   gdb.printing.FlagEnumerationPrinter('enum flag_enum'))
+
     return pp
 
 

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: RFC: enum pretty-printing support
  2012-01-11 21:25             ` Tom Tromey
@ 2012-01-16 18:44               ` Tom Tromey
  2012-01-16 18:45                 ` Eli Zaretskii
  0 siblings, 1 reply; 13+ messages in thread
From: Tom Tromey @ 2012-01-16 18:44 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches, Eli Zaretskii

This updated version needs a doc review.
I minorly changed the texi and I added a NEWS entry.

Tom> 	* NEWS: Add entries.

Tom> b/gdb/doc/ChangeLog:
Tom> 2012-01-11  Tom Tromey  <tromey@redhat.com>

Tom> 	* gdb.texinfo (gdb.printing): Document FlagEnumerationPrinter.

Tom

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: RFC: enum pretty-printing support
  2012-01-16 18:44               ` Tom Tromey
@ 2012-01-16 18:45                 ` Eli Zaretskii
  0 siblings, 0 replies; 13+ messages in thread
From: Eli Zaretskii @ 2012-01-16 18:45 UTC (permalink / raw)
  To: Tom Tromey; +Cc: jan.kratochvil, gdb-patches

> From: Tom Tromey <tromey@redhat.com>
> Cc: gdb-patches@sourceware.org, Eli Zaretskii <eliz@elta.co.il>
> Date: Mon, 16 Jan 2012 10:23:07 -0700
> 
> This updated version needs a doc review.
> I minorly changed the texi and I added a NEWS entry.
> 
> Tom> 	* NEWS: Add entries.
> 
> Tom> b/gdb/doc/ChangeLog:
> Tom> 2012-01-11  Tom Tromey  <tromey@redhat.com>
> 
> Tom> 	* gdb.texinfo (gdb.printing): Document FlagEnumerationPrinter.

OK.

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2012-01-16 18:44 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-01-10 20:26 RFC: enum pretty-printing support Tom Tromey
2012-01-10 20:28 ` Pedro Alves
2012-01-11 16:45   ` Tom Tromey
2012-01-10 21:19 ` Eli Zaretskii
2012-01-10 21:23 ` Jan Kratochvil
2012-01-10 22:00   ` Tom Tromey
2012-01-10 22:19     ` Tom Tromey
2012-01-10 23:52       ` Jan Kratochvil
2012-01-11 16:46         ` Tom Tromey
2012-01-11 21:03           ` Jan Kratochvil
2012-01-11 21:25             ` Tom Tromey
2012-01-16 18:44               ` Tom Tromey
2012-01-16 18:45                 ` Eli Zaretskii

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).