gdb: Add new 'print nibbles' feature
authorEnze Li <enze.li@hotmail.com>
Sun, 12 Jun 2022 05:22:25 +0000 (13:22 +0800)
committerEnze Li <enze.li@hotmail.com>
Sat, 18 Jun 2022 03:23:06 +0000 (11:23 +0800)
Make an introduction of a new print setting that can be set by 'set
print nibbles [on|off]'.  The default value if OFF, which can be changed
by user manually.  Of course, 'show print nibbles' is also included in
the patch.

The new feature displays binary values by group, with four bits per
group.  The motivation for this work is to enhance the readability of
binary values.

Here's a GDB session before this patch is applied.
  (gdb) print var_a
  $1 = 1230
  (gdb) print/t var_a
  $2 = 10011001110

With this patch applied, we can use the new print setting to display the
new form of the binary values.
  (gdb) print var_a
  $1 = 1230
  (gdb) print/t var_a
  $2 = 10011001110
  (gdb) set print nibbles on
  (gdb) print/t var_a
  $3 = 0100 1100 1110

Tested on x86_64 openSUSE Tumbleweed.

gdb/c-lang.c
gdb/language.h
gdb/printcmd.c
gdb/rust-lang.h
gdb/testsuite/gdb.base/options.exp
gdb/testsuite/gdb.base/printcmds.exp
gdb/valprint.c
gdb/valprint.h

index 768963529544ceb9f40b4254565dbccda9c2c946..87373085f24b50f81027d2174dff0ddb6ab45121 100644 (file)
@@ -857,6 +857,10 @@ public:
   const char *natural_name () const override
   { return "C++"; }
 
+  /* See language.h  */
+  const char *get_digit_separator () const override
+  { return "\'"; }
+
   /* See language.h.  */
 
   const std::vector<const char *> &filename_extensions () const override
index f2885000259b2df35dbd0f555a15323f88c22630..c6812c526347ccf0000b171af45fb64a56d91d92 100644 (file)
@@ -282,6 +282,13 @@ struct language_defn
 
   virtual const char *natural_name () const = 0;
 
+  /* Digit separator of the language.  */
+
+  virtual const char *get_digit_separator () const
+  {
+    return " ";
+  }
+
   /* Return a vector of file extensions for this language.  The extension
      must include the ".", like ".c".  If this language doesn't need to
      provide any filename extensions, this may be an empty vector (which is
index 806c5d1b004f9f9dec4f97007cae26bb1821c415..ce3dd3882d9e055919aca88542a1577d2d61fc37 100644 (file)
@@ -485,7 +485,7 @@ print_scalar_formatted (const gdb_byte *valaddr, struct type *type,
       break;
 
     case 't':
-      print_binary_chars (stream, valaddr, len, byte_order, size > 0);
+      print_binary_chars (stream, valaddr, len, byte_order, size > 0, options);
       break;
     case 'x':
       print_hex_chars (stream, valaddr, len, byte_order, size > 0);
index 514494a2c82ff1b0d0b20f85e20a41a3b33e5aee..6da57f8633fddd2ea2d5c1268b3b113ac2f47c74 100644 (file)
@@ -71,6 +71,11 @@ public:
 
   /* See language.h.  */
 
+  const char *get_digit_separator () const override
+  { return "_"; }
+
+  /* See language.h.  */
+
   const std::vector<const char *> &filename_extensions () const override
   {
     static const std::vector<const char *> extensions = { ".rs" };
index 414666142828c69504b76902b6432387d1315185..d10e7f8d66f645e88720f0bab0968a2d7ba6cda0 100644 (file)
@@ -168,6 +168,7 @@ proc_with_prefix test-print {{prefix ""}} {
        "-elements"
        "-max-depth"
        "-memory-tag-violations"
+       "-nibbles"
        "-null-stop"
        "-object"
        "-pretty"
index 3260c8a3c095cdf5dd12f0a9b698904cbcd626f1..04d390d365840717107c1f91708aab8189dd19b4 100644 (file)
@@ -709,6 +709,48 @@ proc test_print_char_arrays {} {
     gdb_test_no_output "set print address off" "address off char arrays"
 }
 
+proc test_print_nibbles {} {
+    gdb_test_no_output "set print nibbles on"
+    foreach lang_line {
+       {"ada" "0" "0" "0011 0000" "1111 1111" "1111" "0001" "1111 0000 1111"}
+       {"asm" "0" "0" "0011 0000" "1111 1111" "1111" "0001" "1111 0000 1111"}
+       {"c" "0" "0" "0011 0000" "1111 1111" "1111" "0001" "1111 0000 1111"}
+       {"c++" "0" "0" "0011'0000" "1111'1111" "1111" "0001" "1111'0000'1111"}
+       {"d" "0" "0" "0011 0000" "1111 1111" "1111" "0001" "1111 0000 1111"}
+       {"fortran" "0" "0" "0011 0000" "1111 1111" "1111" "0001" "1111 0000 1111"}
+       {"go" "0" "0" "0011 0000" "1111 1111" "1111" "0001" "1111 0000 1111"}
+       {"minimal" "0" "0" "0011 0000" "1111 1111" "1111" "0001" "1111 0000 1111"}
+       {"objective-c" "0" "0" "0011 0000" "1111 1111" "1111" "0001" "1111 0000 1111"}
+       {"opencl" "0" "0" "0011 0000" "1111 1111" "1111" "0001" "1111 0000 1111"}
+       {"pascal" "0" "0" "0011 0000" "1111 1111" "1111" "0001" "1111 0000 1111"}
+       {"rust" "0" "0" "0011_0000" "1111_1111" "1111" "0001" "1111_0000_1111"}
+    } {
+       set lang [lindex $lang_line 0]
+       set val1 [lindex $lang_line 1]
+       set val2 [lindex $lang_line 2]
+       set val3 [lindex $lang_line 3]
+       set val4 [lindex $lang_line 4]
+       set val5 [lindex $lang_line 5]
+       set val6 [lindex $lang_line 6]
+       set val7 [lindex $lang_line 7]
+       set val8 [lindex $lang_line 8]
+
+       with_test_prefix "$lang" {
+           gdb_test_no_output "set language $lang"
+           gdb_test "p/t 0" $val1
+           gdb_test "p/t 0x0" $val2
+           gdb_test "p/t 0x30" $val3
+           gdb_test "p/t 0xff" $val4
+           gdb_test "p/t 0x0f" $val5
+           gdb_test "p/t 0x01" $val6
+           gdb_test "p/t 0xf0f" $val7
+           gdb_test "p/t 0x70f" $val8
+           gdb_test_no_output "set language auto"
+       }
+    }
+    gdb_test_no_output "set print nibbles off"
+}
+
 proc test_print_string_constants {} {
     global gdb_prompt
 
@@ -1086,6 +1128,7 @@ test_print_int_arrays
 test_print_typedef_arrays
 test_artificial_arrays
 test_print_char_arrays
+test_print_nibbles
 # We used to do the runto main here.
 test_print_string_constants
 test_print_array_constants
index 471146769342a8874e57ca76ad318940c934b397..e63ec30f4c7c6f8ec18eaf4b68f7bf603ac38366 100644 (file)
@@ -105,6 +105,7 @@ struct value_print_options user_print_options =
   0,                           /* vtblprint */
   1,                           /* unionprint */
   1,                           /* addressprint */
+  false,                       /* nibblesprint */
   0,                           /* objectprint */
   PRINT_MAX_DEFAULT,           /* print_max */
   10,                          /* repeat_count_threshold */
@@ -257,6 +258,17 @@ show_unionprint (struct ui_file *file, int from_tty,
              value);
 }
 
+/* Controls the format of printing binary values.  */
+
+static void
+show_nibbles (struct ui_file *file, int from_tty,
+                      struct cmd_list_element *c, const char *value)
+{
+  gdb_printf (file,
+             _("Printing binary values in groups is %s.\n"),
+             value);
+}
+
 /* If nonzero, causes machine addresses to be printed in certain contexts.  */
 
 static void
@@ -1357,18 +1369,23 @@ print_floating (const gdb_byte *valaddr, struct type *type,
 
 void
 print_binary_chars (struct ui_file *stream, const gdb_byte *valaddr,
-                   unsigned len, enum bfd_endian byte_order, bool zero_pad)
+                   unsigned len, enum bfd_endian byte_order, bool zero_pad,
+                   const struct value_print_options *options)
 {
   const gdb_byte *p;
   unsigned int i;
   int b;
   bool seen_a_one = false;
+  const char *digit_separator = nullptr;
 
   /* Declared "int" so it will be signed.
      This ensures that right shift will shift in zeros.  */
 
   const int mask = 0x080;
 
+  if (options->nibblesprint)
+    digit_separator = current_language->get_digit_separator();
+
   if (byte_order == BFD_ENDIAN_BIG)
     {
       for (p = valaddr;
@@ -1380,6 +1397,9 @@ print_binary_chars (struct ui_file *stream, const gdb_byte *valaddr,
 
          for (i = 0; i < (HOST_CHAR_BIT * sizeof (*p)); i++)
            {
+             if (options->nibblesprint && seen_a_one && i % 4 == 0)
+               gdb_putc (*digit_separator, stream);
+
              if (*p & (mask >> i))
                b = '1';
              else
@@ -1387,6 +1407,13 @@ print_binary_chars (struct ui_file *stream, const gdb_byte *valaddr,
 
              if (zero_pad || seen_a_one || b == '1')
                gdb_putc (b, stream);
+             else if (options->nibblesprint)
+               {
+                 if ((0xf0 & (mask >> i) && (*p & 0xf0))
+                     || (0x0f & (mask >> i) && (*p & 0x0f)))
+                   gdb_putc (b, stream);
+               }
+
              if (b == '1')
                seen_a_one = true;
            }
@@ -1400,6 +1427,9 @@ print_binary_chars (struct ui_file *stream, const gdb_byte *valaddr,
        {
          for (i = 0; i < (HOST_CHAR_BIT * sizeof (*p)); i++)
            {
+             if (options->nibblesprint && seen_a_one && i % 4 == 0)
+               gdb_putc (*digit_separator, stream);
+
              if (*p & (mask >> i))
                b = '1';
              else
@@ -1407,6 +1437,13 @@ print_binary_chars (struct ui_file *stream, const gdb_byte *valaddr,
 
              if (zero_pad || seen_a_one || b == '1')
                gdb_putc (b, stream);
+             else if (options->nibblesprint)
+               {
+                 if ((0xf0 & (mask >> i) && (*p & 0xf0))
+                     || (0x0f & (mask >> i) && (*p & 0x0f)))
+                   gdb_putc (b, stream);
+               }
+
              if (b == '1')
                seen_a_one = true;
            }
@@ -2837,6 +2874,15 @@ static const gdb::option::option_def value_print_option_defs[] = {
     NULL, /* help_doc */
   },
 
+  boolean_option_def {
+    "nibbles",
+    [] (value_print_options *opt) { return &opt->nibblesprint; },
+    show_nibbles, /* show_cmd_cb */
+    N_("Set whether to print binary values in groups of four bits."),
+    N_("Show whether to print binary values in groups of four bits."),
+    NULL, /* help_doc */
+  },
+
   uinteger_option_def {
     "elements",
     [] (value_print_options *opt) { return &opt->print_max; },
index ff536fbe4f097cf59eccb576b711c3d73c5b3eeb..d6ad45d7580fdeb4de8b4eb450bb08e679303517 100644 (file)
@@ -44,6 +44,9 @@ struct value_print_options
   /* Controls printing of addresses.  */
   bool addressprint;
 
+  /* Controls printing of nibbles.  */
+  bool nibblesprint;
+
   /* Controls looking up an object's derived type using what we find
      in its vtables.  */
   bool objectprint;
@@ -149,7 +152,8 @@ extern void value_print_scalar_formatted
    int size, struct ui_file *stream);
 
 extern void print_binary_chars (struct ui_file *, const gdb_byte *,
-                               unsigned int, enum bfd_endian, bool);
+                               unsigned int, enum bfd_endian, bool,
+                               const struct value_print_options *options);
 
 extern void print_octal_chars (struct ui_file *, const gdb_byte *,
                               unsigned int, enum bfd_endian);