[PING][PATCH v7] gdb: Add new 'print nibbles' feature

Enze Li lienze2010@hotmail.com
Tue Jan 11 14:06:41 GMT 2022


Hi all,

Kindly PING for this patch.  :)

Also, I'm not sure if the patch of this size needs to be split into
smaller patches for easier review, please let me know if so.

Cheers!
Enze

On Wed, 2021-12-29 at 21:45 +0800, Enze Li via Gdb-patches wrote:
> New in v7:
> - Address Tom Tromey's comments. Use specific digit
>   separators when debugging C++ and Rust programs.
> 
> New in v6:
> - Address Eli's comments. Use a new complementary method
>   with the number 0.
> 
> New in v5:
> - Address Bruno's comments. Fix regressions caused by code
>   conflicts.
> 
> Make an introduction of a new print setting that can be set
> by 'set print nibbles [on|off]'.  The default value is OFF,
> which can be changed by users manually.
> 
> Of course, 'show print nibbles' is also included in the patch.
> 
> This new feature displays binary values by group, with four
> bits per group.
> 
> The motivation behind 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 have a new print setting to use.
>   (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-linux(little-endian) and mips-linux(big-endian).
> ---
>  gdb/NEWS                                |  5 ++
>  gdb/doc/gdb.texinfo                     | 35 ++++++++++--
>  gdb/printcmd.c                          |  2 +-
>  gdb/testsuite/gdb.base/options.exp      |  1 +
>  gdb/testsuite/gdb.base/printcmds.exp    | 14 +++++
>  gdb/testsuite/gdb.cp/printnibbles.cc    | 24 +++++++++
>  gdb/testsuite/gdb.cp/printnibbles.exp   | 55 +++++++++++++++++++
>  gdb/testsuite/gdb.rust/printnibbles.exp | 46 ++++++++++++++++
>  gdb/testsuite/gdb.rust/printnibbles.rs  | 23 ++++++++
>  gdb/valprint.c                          | 71
> ++++++++++++++++++++++++-
>  gdb/valprint.h                          |  6 ++-
>  11 files changed, 275 insertions(+), 7 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.cp/printnibbles.cc
>  create mode 100644 gdb/testsuite/gdb.cp/printnibbles.exp
>  create mode 100644 gdb/testsuite/gdb.rust/printnibbles.exp
>  create mode 100644 gdb/testsuite/gdb.rust/printnibbles.rs
> 
> diff --git a/gdb/NEWS b/gdb/NEWS
> index c26e15b530a..c23560d1ced 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -257,6 +257,11 @@ set debug event-loop
>  show debug event-loop
>    Control the display of debug output about GDB's event loop.
>  
> +set print nibbles [on|off]
> +show print nibbles
> +  This controls whether the 'print/t' command will display binary
> values
> +  in groups of four bits, known as "nibbles".  The default is 'off'.
> +
>  set print memory-tag-violations
>  show print memory-tag-violations
>    Control whether to display additional information about memory tag
> violations
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 77cd184c47b..2afcd16eadf 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -2142,10 +2142,10 @@ on @code{-} after the command name.  For
> example:
>  
>  @smallexample
>  (@value{GDBP}) print -@key{TAB}@key{TAB}
> --address         -max-depth               -pretty          -symbol
> --array           -memory-tag-violations   -raw-values      -union
> --array-indexes   -null-stop               -repeats         -vtbl
> --elements        -object                  -static-members
> +-address         -max-depth               -object          -static-
> members
> +-array           -memory-tag-violations   -pretty          -symbol
> +-array-indexes   -nibbles                 -raw-values      -union
> +-elements        -null-stop               -repeats         -vtbl
>  @end smallexample
>  
>  Completion will in some cases guide you with a suggestion of what
> kind
> @@ -10019,6 +10019,10 @@ Set limit on string chars or array elements
> to print.  The value
>  Set the threshold after which nested structures are replaced with
>  ellipsis.  Related setting: @ref{set print max-depth}.
>  
> +@item -nibbles [@code{on}|@code{off}]
> +Set whether to print binary values in groups of four bits, known
> +as ``nibbles''.  @xref{set print nibbles}.
> +
>  @item -memory-tag-violations [@code{on}|@code{off}]
>  Set printing of additional information about memory tag violations.
>  @xref{set print memory-tag-violations}.
> @@ -11381,6 +11385,29 @@ Stop printing element indexes when
> displaying arrays.
>  Show whether the index of each element is printed when displaying
>  arrays.
>  
> +@anchor{set print nibbles}
> +@item set print nibbles
> +@itemx set print nibbles on
> +@cindex print binary values in groups of four bits
> +Print binary values in groups of four bits, known as @dfn{nibbles},
> +when using the print command of @value{GDBN} with the option
> @samp{/t}.
> +For example, this is what it looks like with @code{set print nibbles
> on}:
> +
> +@smallexample
> +@group
> +(@value{GDBP}) print val_flags
> +$1 = 1230
> +(@value{GDBP}) print/t val_flags
> +$2 = 0100 1100 1110
> +@end group
> +@end smallexample
> +
> +@item set print nibbles off
> +Don't printing binary values in groups.  This is the default.
> +
> +@item show print nibbles
> +Show whether to print binary values in groups of four bits.
> +
>  @anchor{set print elements}
>  @item set print elements @var{number-of-elements}
>  @itemx set print elements unlimited
> diff --git a/gdb/printcmd.c b/gdb/printcmd.c
> index e408b19db63..ef1a80ecae3 100644
> --- a/gdb/printcmd.c
> +++ b/gdb/printcmd.c
> @@ -490,7 +490,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);
> diff --git a/gdb/testsuite/gdb.base/options.exp
> b/gdb/testsuite/gdb.base/options.exp
> index 94372aa9fc7..1a680a3baf4 100644
> --- a/gdb/testsuite/gdb.base/options.exp
> +++ b/gdb/testsuite/gdb.base/options.exp
> @@ -168,6 +168,7 @@ proc_with_prefix test-print {{prefix ""}} {
>         "-elements"
>         "-max-depth"
>         "-memory-tag-violations"
> +       "-nibbles"
>         "-null-stop"
>         "-object"
>         "-pretty"
> diff --git a/gdb/testsuite/gdb.base/printcmds.exp
> b/gdb/testsuite/gdb.base/printcmds.exp
> index b2f90aaff10..0b015b27e28 100644
> --- a/gdb/testsuite/gdb.base/printcmds.exp
> +++ b/gdb/testsuite/gdb.base/printcmds.exp
> @@ -690,6 +690,19 @@ 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"
> +    gdb_test "p/t 0" " = 0"
> +    gdb_test "p/t 0x0" " = 0"
> +    gdb_test "p/t 0x30" " = 0011 0000"
> +    gdb_test "p/t 0xff" " = 1111 1111"
> +    gdb_test "p/t 0x0f" " = 1111"
> +    gdb_test "p/t 0x01" " = 0001"
> +    gdb_test "p/t 0xf0f" " = 1111 0000 1111"
> +    gdb_test "p/t 0x70f" " = 0111 0000 1111"
> +    gdb_test_no_output "set print nibbles off"
> +}
> +
>  proc test_print_string_constants {} {
>      global gdb_prompt
>  
> @@ -1067,6 +1080,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
> diff --git a/gdb/testsuite/gdb.cp/printnibbles.cc
> b/gdb/testsuite/gdb.cp/printnibbles.cc
> new file mode 100644
> index 00000000000..c82c344c6d4
> --- /dev/null
> +++ b/gdb/testsuite/gdb.cp/printnibbles.cc
> @@ -0,0 +1,24 @@
> +/* This test script is part of GDB, the GNU debugger.
> +
> +   Copyright 2021 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
> <http://www.gnu.org/licenses/>.  */
> +
> +int main()
> +{
> +  /* Create an array to test the output of 'print nibbles' */
> +  unsigned int print_arr[] = {0, 0x0, 0x30, 0xff, 0x0f, 0x01, 0xf0f,
> 0x70f};
> +
> +  return 0;                            // breakpoint: constructs-
> done
> +}
> diff --git a/gdb/testsuite/gdb.cp/printnibbles.exp
> b/gdb/testsuite/gdb.cp/printnibbles.exp
> new file mode 100644
> index 00000000000..575cadccc1c
> --- /dev/null
> +++ b/gdb/testsuite/gdb.cp/printnibbles.exp
> @@ -0,0 +1,55 @@
> +# Copyright 2021 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
> <http://www.gnu.org/licenses/>.
> +
> +# Regression test for 'print nibbles' commands.
> +
> +if { [skip_cplus_tests] } { continue }
> +
> +#
> +# test running programs
> +#
> +
> +standard_testfile .cc
> +
> +if [get_compiler_info "c++"] {
> +    return -1
> +}
> +
> +if {[prepare_for_testing "failed to prepare" $testfile $srcfile
> {debug c++}]} {
> +    return -1
> +}
> +
> +if ![runto_main] then {
> +    perror "couldn't run to breakpoint"
> +    continue
> +}
> +
> +gdb_breakpoint [gdb_get_line_number "constructs-done"]
> +gdb_continue_to_breakpoint "end of constructors"
> +
> +# test the output of 'print nibbles'
> +gdb_test_no_output "set print nibbles on"
> +gdb_test "p/t print_arr\[0\]" " = 0"
> +gdb_test "p/t print_arr\[1\]" " = 0"
> +gdb_test "p/t print_arr\[2\]" " = 0011'0000"
> +gdb_test "p/t print_arr\[3\]" " = 1111'1111"
> +gdb_test "p/t print_arr\[4\]" " = 1111"
> +gdb_test "p/t print_arr\[5\]" " = 0001"
> +gdb_test "p/t print_arr\[6\]" " = 1111'0000'1111"
> +gdb_test "p/t print_arr\[7\]" " = 0111'0000'1111"
> +gdb_test_no_output "set print nibbles off"
> +
> +gdb_exit
> +return 0
> diff --git a/gdb/testsuite/gdb.rust/printnibbles.exp
> b/gdb/testsuite/gdb.rust/printnibbles.exp
> new file mode 100644
> index 00000000000..5d324164028
> --- /dev/null
> +++ b/gdb/testsuite/gdb.rust/printnibbles.exp
> @@ -0,0 +1,46 @@
> +# Copyright 2021 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
> <http://www.gnu.org/licenses/>.
> +
> +# Regression test for 'print nibbles' commands.
> +
> +load_lib rust-support.exp
> +if {[skip_rust_tests]} {
> +    continue
> +}
> +
> +standard_testfile .rs
> +if {[prepare_for_testing "failed to prepare" $testfile $srcfile \
> +         {debug rust}]} {
> +    return -1
> +}
> +
> +set line [gdb_get_line_number "breakpoint"]
> +if {![runto ${srcfile}:$line]} {
> +    untested "could not run to breakpoint"
> +    return -1
> +}
> +
> +# test the output of 'print nibbles'
> +gdb_test_no_output "set print nibbles on"
> +gdb_test "p/t print_arr\[0\]" " = 0"
> +gdb_test "p/t print_arr\[1\]" " = 0"
> +gdb_test "p/t print_arr\[2\]" " = 0011_0000"
> +gdb_test "p/t print_arr\[3\]" " = 1111_1111"
> +gdb_test "p/t print_arr\[4\]" " = 1111"
> +gdb_test "p/t print_arr\[5\]" " = 0001"
> +gdb_test "p/t print_arr\[6\]" " = 1111_0000_1111"
> +gdb_test "p/t print_arr\[7\]" " = 0111_0000_1111"
> +gdb_test_no_output "set print nibbles off"
> +
> diff --git a/gdb/testsuite/gdb.rust/printnibbles.rs
> b/gdb/testsuite/gdb.rust/printnibbles.rs
> new file mode 100644
> index 00000000000..d3fe9e04306
> --- /dev/null
> +++ b/gdb/testsuite/gdb.rust/printnibbles.rs
> @@ -0,0 +1,23 @@
> +// Copyright (C) 2021 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
> <http://www.gnu.org/licenses/>.
> +
> +#![allow(unused_variables)]
> +
> +fn main() {
> +    // Create an array to test the output of 'print nibbles'
> +    let print_arr:[u32;8] = [0, 0x0, 0x30, 0xff, 0x0f, 0x01, 0xf0f,
> 0x70f];
> +
> +    println!("");               // breakpoint
> +}
> diff --git a/gdb/valprint.c b/gdb/valprint.c
> index 4230dcec228..9931d311a56 100644
> --- a/gdb/valprint.c
> +++ b/gdb/valprint.c
> @@ -97,6 +97,8 @@ static void val_print_type_code_flags (struct type
> *type,
>                                        int embedded_offset,
>                                        struct ui_file *stream);
>  
> +static char get_digit_separator ();
> +
>  #define PRINT_MAX_DEFAULT 200  /* Start print_max off at this
> value.  */
>  #define PRINT_MAX_DEPTH_DEFAULT 20     /* Start print_max_depth off
> at this value. */
>  
> @@ -108,6 +110,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 */
> @@ -260,6 +263,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)
> +{
> +  fprintf_filtered (file,
> +                   _("Printing binary values in groups is %s.\n"),
> +                   value);
> +}
> +
>  /* If nonzero, causes machine addresses to be printed in certain
> contexts.  */
>  
>  static void
> @@ -1365,20 +1379,46 @@ print_floating (const gdb_byte *valaddr,
> struct type *type,
>    fputs_filtered (str.c_str (), stream);
>  }
>  
> +/* Return the digit separator for the current debugging language. */
> +
> +static char
> +get_digit_separator ()
> +{
> +  if (current_language != nullptr)
> +    {
> +      switch (current_language->la_language)
> +       {
> +       case language_cplus:
> +         return '\'';
> +       case language_rust:
> +         return '_';
> +       default:
> +         return ' ';
> +       }
> +    }
> +  else
> +    return ' ';
> +}
> +
>  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;
> +  char digit_separator = ' ';
>  
>    /* 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 = get_digit_separator();
> +
>    if (byte_order == BFD_ENDIAN_BIG)
>      {
>        for (p = valaddr;
> @@ -1390,6 +1430,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)
> +               fputc_filtered (digit_separator, stream);
> +
>               if (*p & (mask >> i))
>                 b = '1';
>               else
> @@ -1397,6 +1440,13 @@ print_binary_chars (struct ui_file *stream,
> const gdb_byte *valaddr,
>  
>               if (zero_pad || seen_a_one || b == '1')
>                 fputc_filtered (b, stream);
> +             else if (options->nibblesprint)
> +               {
> +                 if ((0xf0 & (mask >> i) && (*p & 0xf0)) ||
> +                     (0x0f & (mask >> i) && (*p & 0x0f)))
> +                   fputc_filtered (b, stream);
> +               }
> +
>               if (b == '1')
>                 seen_a_one = true;
>             }
> @@ -1410,6 +1460,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)
> +               fputc_filtered (digit_separator, stream);
> +
>               if (*p & (mask >> i))
>                 b = '1';
>               else
> @@ -1417,6 +1470,13 @@ print_binary_chars (struct ui_file *stream,
> const gdb_byte *valaddr,
>  
>               if (zero_pad || seen_a_one || b == '1')
>                 fputc_filtered (b, stream);
> +             else if (options->nibblesprint)
> +               {
> +                 if ((0xf0 & (mask >> i) && (*p & 0xf0)) ||
> +                     (0x0f & (mask >> i) && (*p & 0x0f)))
> +                   fputc_filtered (b, stream);
> +               }
> +
>               if (b == '1')
>                 seen_a_one = true;
>             }
> @@ -3016,6 +3076,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; },
> diff --git a/gdb/valprint.h b/gdb/valprint.h
> index e1dae2cc8fc..bb19c41ab68 100644
> --- a/gdb/valprint.h
> +++ b/gdb/valprint.h
> @@ -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);



More information about the Gdb-patches mailing list