[PATCH v5] Add negative repeat count to 'x' command

Thomas Preudhomme thomas.preudhomme@foss.arm.com
Mon Aug 8 14:45:00 GMT 2016


Sorry for the late reply but I noticed an issue in the tests added by 
this patch for arm-none-eabi:


FAIL: gdb.base/examine-backward.exp: address zero boundary: examine 3 
bytes forward from 0x0
NA->FAIL: gdb.base/examine-backward.exp char-width=1, print-max=20 take 
1 string backward (6/6)
NA->FAIL: gdb.base/examine-backward.exp char-width=1, print-max=20 take 
6 strings backward (pattern 1)


The first one is a testism:

we match for: 0x\[0-9a-f\]+00.*:${byte}${byte}${byte}
but we get: 0x0:    0x7f    0x45    0x4c

I'd thus suggest to match 0x\(\[0-9a-f\]+0\)?0 instead, with the right 
amount of escaping (I don't know whether ? needs escaping and why + does 
not need escaping in current pattern).


The other two seems like genuine errors. take 6 strings backward 
(pattern 1) gives:

gdb_expect_list pattern: /"ABCDEFGHIJKLMNOPQRST".../
x/-6s^M
0x9aff <_fini+2>: "\277\370\274\b\274\236FpGABCDEFGHIJK"...^M
0x9b13 <TestStrings+11>:        "LMNOPQRSTUVWXYZ"^M
0x9b23 <TestStrings+27>:        ""^M
0x9b24 <TestStrings+28>:        ""^M
0x9b25 <TestStrings+29>: 
"\343\201\273\343\201\222\343\201\273\343\201\222"^M
0x9b32 <TestStrings+42>:        "01234567890123456789"...^M
(gdb) FAIL: gdb.base/examine-backward.exp: char-width=1, print-max=20: 
take 6 strings backward (pattern 1)


take 1 string backward (6/6) gives:

0x9aff <_fini+2>: "\277\370\274\b\274\236FpGABCDEFGHIJK"...


Please let me know what I can do to help diagnose this error.


Best regards,


Thomas


On 02/06/16 09:49, Toshihito Kikuchi wrote:
> This change adds support for specifying a negative repeat count to
> all the formats of the 'x' command to examine memory backward.
> A new testcase 'examine-backward' is added to cover this new feature.
>
> Here's the example output from the new feature:
>
> <format 'i'>
> (gdb) bt
> #0  Func1 (n=42, p=0x40432e "hogehoge") at main.cpp:5
> #1  0x00000000004041fa in main (argc=1, argv=0x7fffffffdff8) at main.cpp:19
> (gdb) x/-4i 0x4041fa
>    0x4041e5 <main(int, char**)+11>: mov   %rsi,-0x10(%rbp)
>    0x4041e9 <main(int, char**)+15>: lea   0x13e(%rip),%rsi
>    0x4041f0 <main(int, char**)+22>: mov   $0x2a,%edi
>    0x4041f5 <main(int, char**)+27>: callq 0x404147
>
> <format 'x'>
> (gdb) x/-4xw 0x404200
> 0x4041f0 <main(int, char**)+22>: 0x00002abf 0xff4de800 0x76e8ffff 0xb8ffffff
> (gdb) x/-4
> 0x4041e0 <main(int, char**)+6>:  0x7d8910ec 0x758948fc 0x358d48f0 0x0000013e
>
> gdb/ChangeLog:
>
> 	* NEWS: Mention that GDB now supports a negative repeat count in
> 	the 'x' command.
> 	* printcmd.c (decode_format): Allow '-' in the parameter
> 	"string_ptr" to accept a negative repeat count.
> 	(find_instruction_backward): New function.
> 	(read_memory_backward): New function.
> 	(integer_is_zero): New function.
> 	(find_string_backward): New function.
> 	(do_examine): Use new functions to examine memory backward.
> 	(_initialize_printcmd): Mention that 'x' command supports a negative
> 	repeat count.
>
> gdb/doc/ChangeLog:
>
> 	* gdb.texinfo (Examining Memory): Document negative repeat
> 	count in the 'x' command.
>
> gdb/testsuite/ChangeLog:
>
> 	* gdb.base/examine-backward.c: New file.
> 	* gdb.base/examine-backward.exp: New file.
> ---
>   gdb/NEWS                                    |  14 ++
>   gdb/doc/gdb.texinfo                         |  14 +-
>   gdb/printcmd.c                              | 262 +++++++++++++++++++++-
>   gdb/testsuite/gdb.base/examine-backward.c   | 106 +++++++++
>   gdb/testsuite/gdb.base/examine-backward.exp | 324 ++++++++++++++++++++++++++++
>   5 files changed, 717 insertions(+), 3 deletions(-)
>   create mode 100644 gdb/testsuite/gdb.base/examine-backward.c
>   create mode 100644 gdb/testsuite/gdb.base/examine-backward.exp
>
> diff --git a/gdb/NEWS b/gdb/NEWS
> index dce79a2..6b6cc4f 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -3,6 +3,20 @@
>   
>   *** Changes since GDB 7.11
>   
> +* GDB now supports a negative repeat count in the 'x' command to examine
> +  memory backward from the given address.  For example:
> +
> +    (gdb) bt
> +    #0  Func1 (n=42, p=0x40061c "hogehoge") at main.cpp:4
> +    #1  0x400580 in main (argc=1, argv=0x7fffffffe5c8) at main.cpp:8
> +    (gdb) x/-5i 0x0000000000400580
> +       0x40056a <main(int, char**)+8>:      mov    %edi,-0x4(%rbp)
> +       0x40056d <main(int, char**)+11>:     mov    %rsi,-0x10(%rbp)
> +       0x400571 <main(int, char**)+15>:     mov    $0x40061c,%esi
> +       0x400576 <main(int, char**)+20>:     mov    $0x2a,%edi
> +       0x40057b <main(int, char**)+25>:
> +        callq  0x400536 <Func1(int, char const*)>
> +
>   * Fortran: Support structures with fields of dynamic types and
>     arrays of dynamic types.
>   
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 7e89003..12d0140 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -9295,7 +9295,8 @@ Several commands set convenient defaults for @var{addr}.
>   @table @r
>   @item @var{n}, the repeat count
>   The repeat count is a decimal integer; the default is 1.  It specifies
> -how much memory (counting by units @var{u}) to display.
> +how much memory (counting by units @var{u}) to display.  If a negative
> +number is specified, memory is examined backward from @var{addr}.
>   @c This really is **decimal**; unaffected by 'set radix' as of GDB
>   @c 4.1.2.
>   
> @@ -9350,6 +9351,10 @@ starting at address @code{0x54320}.  @samp{x/4xw $sp} prints the four
>   words (@samp{w}) of memory above the stack pointer (here, @samp{$sp};
>   @pxref{Registers, ,Registers}) in hexadecimal (@samp{x}).
>   
> +You can also specify a negative repeat count to examine memory backward
> +from the given address.  For example, @samp{x/-3uh 0x54320} prints three
> +halfwords (@code{h}) at @code{0x54314}, @code{0x54328}, and @code{0x5431c}.
> +
>   Since the letters indicating unit sizes are all distinct from the
>   letters specifying output formats, you do not have to remember whether
>   unit size or format comes first; either order works.  The output
> @@ -9366,6 +9371,13 @@ follow the last instruction that is within the count.  The command
>   @code{disassemble} gives an alternative way of inspecting machine
>   instructions; see @ref{Machine Code,,Source and Machine Code}.
>   
> +If a negative repeat count is specified for the formats @samp{s} or @samp{i},
> +the command displays null-terminated strings or instructions before the given
> +address as many as the absolute value of the given number.  For the @samp{i}
> +format, we use line number information in the debug info to accurately locate
> +instruction boundaries while disassembling backward.  If line info is not
> +available, the command stops examining memory with an error message.
> +
>   All the defaults for the arguments to @code{x} are designed to make it
>   easy to continue scanning memory with minimal specifications each time
>   you use @code{x}.  For example, after you have inspected three machine
> diff --git a/gdb/printcmd.c b/gdb/printcmd.c
> index f5c4211..d4a4b9e 100644
> --- a/gdb/printcmd.c
> +++ b/gdb/printcmd.c
> @@ -186,8 +186,13 @@ decode_format (const char **string_ptr, int oformat, int osize)
>     val.count = 1;
>     val.raw = 0;
>   
> +  if (*p == '-')
> +    {
> +      val.count = -1;
> +      p++;
> +    }
>     if (*p >= '0' && *p <= '9')
> -    val.count = atoi (p);
> +    val.count *= atoi (p);
>     while (*p >= '0' && *p <= '9')
>       p++;
>   
> @@ -785,6 +790,221 @@ print_address_demangle (const struct value_print_options *opts,
>   }
>   

>   
> +/* Find the address of the instruction that is INST_COUNT instructions before
> +   the instruction at ADDR.
> +   Since some architectures have variable-length instructions, we can't just
> +   simply subtract INST_COUNT * INSN_LEN from ADDR.  Instead, we use line
> +   number information to locate the nearest known instruction boundary,
> +   and disassemble forward from there.  If we go out of the symbol range
> +   during disassembling, we return the lowest address we've got so far and
> +   set the number of instructions read to INST_READ.  */
> +
> +static CORE_ADDR
> +find_instruction_backward (struct gdbarch *gdbarch, CORE_ADDR addr,
> +                           int inst_count, int *inst_read)
> +{
> +  /* The vector PCS is used to store instruction addresses within
> +     a pc range.  */
> +  CORE_ADDR loop_start, loop_end, p;
> +  VEC (CORE_ADDR) *pcs = NULL;
> +  struct symtab_and_line sal;
> +  struct cleanup *cleanup = make_cleanup (VEC_cleanup (CORE_ADDR), &pcs);
> +
> +  *inst_read = 0;
> +  loop_start = loop_end = addr;
> +
> +  /* In each iteration of the outer loop, we get a pc range that ends before
> +     LOOP_START, then we count and store every instruction address of the range
> +     iterated in the loop.
> +     If the number of instructions counted reaches INST_COUNT, return the
> +     stored address that is located INST_COUNT instructions back from ADDR.
> +     If INST_COUNT is not reached, we subtract the number of counted
> +     instructions from INST_COUNT, and go to the next iteration.  */
> +  do
> +    {
> +      VEC_truncate (CORE_ADDR, pcs, 0);
> +      sal = find_pc_sect_line (loop_start, NULL, 1);
> +      if (sal.line <= 0)
> +        {
> +          /* We reach here when line info is not available.  In this case,
> +             we print a message and just exit the loop.  The return value
> +             is calculated after the loop.  */
> +          printf_filtered (_("No line number information available "
> +                             "for address "));
> +          wrap_here ("  ");
> +          print_address (gdbarch, loop_start - 1, gdb_stdout);
> +          printf_filtered ("\n");
> +          break;
> +        }
> +
> +      loop_end = loop_start;
> +      loop_start = sal.pc;
> +
> +      /* This loop pushes instruction addresses in the range from
> +         LOOP_START to LOOP_END.  */
> +      for (p = loop_start; p < loop_end;)
> +        {
> +          VEC_safe_push (CORE_ADDR, pcs, p);
> +          p += gdb_insn_length (gdbarch, p);
> +        }
> +
> +      inst_count -= VEC_length (CORE_ADDR, pcs);
> +      *inst_read += VEC_length (CORE_ADDR, pcs);
> +    }
> +  while (inst_count > 0);
> +
> +  /* After the loop, the vector PCS has instruction addresses of the last
> +     source line we processed, and INST_COUNT has a negative value.
> +     We return the address at the index of -INST_COUNT in the vector for
> +     the reason below.
> +     Let's assume the following instruction addresses and run 'x/-4i 0x400e'.
> +       Line X of File
> +          0x4000
> +          0x4001
> +          0x4005
> +       Line Y of File
> +          0x4009
> +          0x400c
> +       => 0x400e
> +          0x4011
> +     find_instruction_backward is called with INST_COUNT = 4 and expected to
> +     return 0x4001.  When we reach here, INST_COUNT is set to -1 because
> +     it was subtracted by 2 (from Line Y) and 3 (from Line X).  The value
> +     4001 is located at the index 1 of the last iterated line (= Line X),
> +     which is simply calculated by -INST_COUNT.
> +     The case when the length of PCS is 0 means that we reached an area for
> +     which line info is not available.  In such case, we return LOOP_START,
> +     which was the lowest instruction address that had line info.  */
> +  p = VEC_length (CORE_ADDR, pcs) > 0
> +    ? VEC_index (CORE_ADDR, pcs, -inst_count)
> +    : loop_start;
> +
> +  /* INST_READ includes all instruction addresses in a pc range.  Need to
> +     exclude the beginning part up to the address we're returning.  That
> +     is, exclude {0x4000} in the example above.  */
> +  if (inst_count < 0)
> +    *inst_read += inst_count;
> +
> +  do_cleanups (cleanup);
> +  return p;
> +}
> +
> +/* Backward read LEN bytes of target memory from address MEMADDR + LEN,
> +   placing the results in GDB's memory from MYADDR + LEN.  Returns
> +   a count of the bytes actually read.  */
> +
> +static int
> +read_memory_backward (struct gdbarch *gdbarch,
> +                      CORE_ADDR memaddr, gdb_byte *myaddr, int len)
> +{
> +  int errcode;
> +  int nread;      /* Number of bytes actually read.  */
> +
> +  /* First try a complete read.  */
> +  errcode = target_read_memory (memaddr, myaddr, len);
> +  if (errcode == 0)
> +    {
> +      /* Got it all.  */
> +      nread = len;
> +    }
> +  else
> +    {
> +      /* Loop, reading one byte at a time until we get as much as we can.  */
> +      memaddr += len;
> +      myaddr += len;
> +      for (nread = 0; nread < len; ++nread)
> +        {
> +          errcode = target_read_memory (--memaddr, --myaddr, 1);
> +          if (errcode != 0)
> +            {
> +              /* The read was unsuccessful, so exit the loop.  */
> +              printf_filtered (_("Cannot access memory at address %s\n"),
> +                               paddress (gdbarch, memaddr));
> +              break;
> +            }
> +        }
> +    }
> +  return nread;
> +}
> +
> +/* Returns true if X (which is LEN bytes wide) is the number zero.  */
> +
> +static int
> +integer_is_zero (const gdb_byte *x, int len)
> +{
> +  int i = 0;
> +
> +  while (i < len && x[i] == 0)
> +    ++i;
> +  return (i == len);
> +}
> +
> +/* Find the start address of a string in which ADDR is included.
> +   Basically we search for '\0' and return the next address,
> +   but if OPTIONS->PRINT_MAX is smaller than the length of a string,
> +   we stop searching and return the address to print characters as many as
> +   PRINT_MAX from the string.  */
> +
> +static CORE_ADDR
> +find_string_backward (struct gdbarch *gdbarch,
> +                      CORE_ADDR addr, int count, int char_size,
> +                      const struct value_print_options *options,
> +                      int *strings_counted)
> +{
> +  const int chunk_size = 0x20;
> +  gdb_byte *buffer = NULL;
> +  struct cleanup *cleanup = NULL;
> +  int read_error = 0;
> +  int chars_read = 0;
> +  int chars_to_read = chunk_size;
> +  int chars_counted = 0;
> +  int count_original = count;
> +  CORE_ADDR string_start_addr = addr;
> +
> +  gdb_assert (char_size == 1 || char_size == 2 || char_size == 4);
> +  buffer = (gdb_byte *) xmalloc (chars_to_read * char_size);
> +  cleanup = make_cleanup (xfree, buffer);
> +  while (count > 0 && read_error == 0)
> +    {
> +      int i;
> +
> +      addr -= chars_to_read * char_size;
> +      chars_read = read_memory_backward (gdbarch, addr, buffer,
> +                                         chars_to_read * char_size);
> +      chars_read /= char_size;
> +      read_error = (chars_read == chars_to_read) ? 0 : 1;
> +      /* Searching for '\0' from the end of buffer in backward direction.  */
> +      for (i = 0; i < chars_read && count > 0 ; ++i, ++chars_counted)
> +        {
> +          int offset = (chars_to_read - i - 1) * char_size;
> +
> +          if (integer_is_zero (buffer + offset, char_size)
> +              || chars_counted == options->print_max)
> +            {
> +              /* Found '\0' or reached print_max.  As OFFSET is the offset to
> +                 '\0', we add CHAR_SIZE to return the start address of
> +                 a string.  */
> +              --count;
> +              string_start_addr = addr + offset + char_size;
> +              chars_counted = 0;
> +            }
> +        }
> +    }
> +
> +  /* Update STRINGS_COUNTED with the actual number of loaded strings.  */
> +  *strings_counted = count_original - count;
> +
> +  if (read_error != 0)
> +    {
> +      /* In error case, STRING_START_ADDR is pointing to the string that
> +         was last successfully loaded.  Rewind the partially loaded string.  */
> +      string_start_addr -= chars_counted * char_size;
> +    }
> +
> +  do_cleanups (cleanup);
> +  return string_start_addr;
> +}
> +
>   /* Examine data at address ADDR in format FMT.
>      Fetch it from memory and print on gdb_stdout.  */
>   
> @@ -798,6 +1018,8 @@ do_examine (struct format_data fmt, struct gdbarch *gdbarch, CORE_ADDR addr)
>     int i;
>     int maxelts;
>     struct value_print_options opts;
> +  int need_to_update_next_address = 0;
> +  CORE_ADDR addr_rewound = 0;
>   
>     format = fmt.format;
>     size = fmt.size;
> @@ -868,6 +1090,38 @@ do_examine (struct format_data fmt, struct gdbarch *gdbarch, CORE_ADDR addr)
>   
>     get_formatted_print_options (&opts, format);
>   
> +  if (count < 0)
> +    {
> +      /* This is the negative repeat count case.
> +         We rewind the address based on the given repeat count and format,
> +         then examine memory from there in forward direction.  */
> +
> +      count = -count;
> +      if (format == 'i')
> +        {
> +          next_address = find_instruction_backward (gdbarch, addr, count,
> +                                                    &count);
> +        }
> +      else if (format == 's')
> +        {
> +          next_address = find_string_backward (gdbarch, addr, count,
> +                                               TYPE_LENGTH (val_type),
> +                                               &opts, &count);
> +        }
> +      else
> +        {
> +          next_address = addr - count * TYPE_LENGTH (val_type);
> +        }
> +
> +      /* The following call to print_formatted updates next_address in every
> +         iteration.  In backward case, we store the start address here
> +         and update next_address with it before exiting the function.  */
> +      addr_rewound = (format == 's'
> +                      ? next_address - TYPE_LENGTH (val_type)
> +                      : next_address);
> +      need_to_update_next_address = 1;
> +    }
> +
>     /* Print as many objects as specified in COUNT, at most maxelts per line,
>        with the address of the next one at the start of each line.  */
>   
> @@ -913,6 +1167,9 @@ do_examine (struct format_data fmt, struct gdbarch *gdbarch, CORE_ADDR addr)
>         printf_filtered ("\n");
>         gdb_flush (gdb_stdout);
>       }
> +
> +  if (need_to_update_next_address)
> +    next_address = addr_rewound;
>   }
>   

>   static void
> @@ -2522,7 +2779,8 @@ Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),\n\
>     and z(hex, zero padded on the left).\n\
>   Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes).\n\
>   The specified number of objects of the specified size are printed\n\
> -according to the format.\n\n\
> +according to the format.  If a negative number is specified, memory is\n\
> +examined backward from the address.\n\n\
>   Defaults for format and size letters are those previously used.\n\
>   Default count is 1.  Default address is following last thing printed\n\
>   with this command or \"print\"."));
> diff --git a/gdb/testsuite/gdb.base/examine-backward.c b/gdb/testsuite/gdb.base/examine-backward.c
> new file mode 100644
> index 0000000..b338503
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/examine-backward.c
> @@ -0,0 +1,106 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2015-2016 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/>.  */
> +
> +/*
> +Define TestStrings, TestStringsH, and TestStringsW to test utf8, utf16,
> +and utf32 strings respectively.
> +To avoid compile errors due to old compiler mode, we don't use string
> +literals.  The content of each array is the same as followings:
> +
> +  const char TestStrings[] = {
> +      "ABCD"
> +      "EFGHIJKLMNOPQRSTUVWXYZ\0"
> +      "\0"
> +      "\0"
> +      "\u307B\u3052\u307B\u3052\0"
> +      "012345678901234567890123456789\0"
> +      "!!!!!!\0"
> +  };
> +*/
> +
> +const char TestStrings[] = {
> +  0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
> +  0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
> +  0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
> +  0x59, 0x5a, 0x00, 0x00, 0x00, 0xe3, 0x81, 0xbb,
> +  0xe3, 0x81, 0x92, 0xe3, 0x81, 0xbb, 0xe3, 0x81,
> +  0x92, 0x00, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
> +  0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33,
> +  0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31,
> +  0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
> +  0x00, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x00,
> +  0x00
> +};
> +
> +const short TestStringsH[] = {
> +  0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048,
> +  0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050,
> +  0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
> +  0x0059, 0x005a, 0x0000, 0x0000, 0x0000, 0x307b, 0x3052, 0x307b,
> +  0x3052, 0x0000, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035,
> +  0x0036, 0x0037, 0x0038, 0x0039, 0x0030, 0x0031, 0x0032, 0x0033,
> +  0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x0030, 0x0031,
> +  0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039,
> +  0x0000, 0x0021, 0x0021, 0x0021, 0x0021, 0x0021, 0x0021, 0x0000,
> +  0x0000
> +};
> +
> +const int TestStringsW[] = {
> +  0x00000041, 0x00000042, 0x00000043, 0x00000044,
> +  0x00000045, 0x00000046, 0x00000047, 0x00000048,
> +  0x00000049, 0x0000004a, 0x0000004b, 0x0000004c,
> +  0x0000004d, 0x0000004e, 0x0000004f, 0x00000050,
> +  0x00000051, 0x00000052, 0x00000053, 0x00000054,
> +  0x00000055, 0x00000056, 0x00000057, 0x00000058,
> +  0x00000059, 0x0000005a, 0x00000000, 0x00000000,
> +  0x00000000, 0x0000307b, 0x00003052, 0x0000307b,
> +  0x00003052, 0x00000000, 0x00000030, 0x00000031,
> +  0x00000032, 0x00000033, 0x00000034, 0x00000035,
> +  0x00000036, 0x00000037, 0x00000038, 0x00000039,
> +  0x00000030, 0x00000031, 0x00000032, 0x00000033,
> +  0x00000034, 0x00000035, 0x00000036, 0x00000037,
> +  0x00000038, 0x00000039, 0x00000030, 0x00000031,
> +  0x00000032, 0x00000033, 0x00000034, 0x00000035,
> +  0x00000036, 0x00000037, 0x00000038, 0x00000039,
> +  0x00000000, 0x00000021, 0x00000021, 0x00000021,
> +  0x00000021, 0x00000021, 0x00000021, 0x00000000,
> +  0x00000000
> +};
> +
> +int
> +main (void)
> +{
> +  /* Backward disassemble test requires at least 20 instructions in
> +     this function.  Adding a simple bubble sort.  */
> +  int i, j;
> +  int n[] = {3, 1, 4, 1, 5, 9};
> +  int len = sizeof (n) / sizeof (n[0]);
> +
> +  for (i = 0; i < len - 1; ++i)
> +    {
> +      for (j = i; j < len; ++j)
> +        {
> +          if (n[j] < n[i])
> +            {
> +              int tmp = n[i];
> +              n[i] = n[j];
> +              n[j] = tmp;
> +            }
> +        }
> +    }
> +  return 42;
> +}
> diff --git a/gdb/testsuite/gdb.base/examine-backward.exp b/gdb/testsuite/gdb.base/examine-backward.exp
> new file mode 100644
> index 0000000..e03cbfd
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/examine-backward.exp
> @@ -0,0 +1,324 @@
> +# Copyright 2015-2016 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/>.
> +
> +# This testsuite is to test examining memory backward by specifying a negative
> +# number in the 'x' command.
> +
> +standard_testfile
> +if { [prepare_for_testing "failed to prepare for examine-backward" \
> +        ${testfile} ${srcfile}] } {
> +    return -1
> +}
> +
> +if ![runto_main] {
> +    untested "could not run to main"
> +    return -1
> +}
> +
> +proc get_first_mapped_address {} {
> +    global gdb_prompt
> +
> +    set addr "0"
> +    gdb_test_multiple "info proc mappings" "info proc mappings" {
> +        -re "objfile\[\r\n\t \]+(0x\[0-9a-fA-F\]+).*\[\r\n\]*$gdb_prompt $" {
> +            set addr $expect_out(1,string)
> +        }
> +        -re "$gdb_prompt $" {
> +            unsupported "Current target does not support 'info proc mappings'"
> +        }
> +    }
> +    return ${addr}
> +}
> +
> +with_test_prefix "invalid format" {
> +    gdb_test "x/- 10xb main" "Invalid number \"10xb\"\." \
> +        "a whitespace after a leading hyphen"
> +    gdb_test "x/--10xb main" "Invalid number \"10xb\"\." \
> +        "double hyphen"
> +    gdb_test "x/-a10xb main" "Invalid number \"10xb\"\." \
> +        "an alphabet after a leading hyphen"
> +    gdb_test_no_output "x/-0i main" "zero with backward disassemble"
> +    gdb_test_no_output "x/-0sh main" "zero with backward examine string"
> +}
> +
> +with_test_prefix "memory page boundary" {
> +    set boundary [get_first_mapped_address]
> +    if {![is_address_zero_readable] && $boundary != 0} {
> +        gdb_test_no_output "set print elements 0"
> +        gdb_test_sequence "x/3s ${boundary}" "take 3 strings forward" {
> +            "0x"
> +            "0x"
> +            "0x"
> +        }
> +        gdb_test_sequence "x/-4s" "take 4 strings backward" {
> +            "Cannot access memory at address 0x"
> +            "0x"
> +            "0x"
> +            "0x"
> +        }
> +        gdb_test_sequence "x/3s ${boundary}" "take 3 strings forward again" {
> +            "0x"
> +            "0x"
> +            "0x"
> +        }
> +        gdb_test_sequence "x/-3s" "take 3 strings backward" {
> +            "Cannot access memory at address 0x"
> +            "0x"
> +            "0x"
> +            "0x"
> +        }
> +    }
> +}
> +
> +with_test_prefix "address zero boundary" {
> +    if {[is_address_zero_readable]} {
> +        set address_zero "0x0"
> +        set byte "\t0x\[0-9a-f\]+"
> +        gdb_test "x/3xb ${address_zero}" \
> +            "0x\[0-9a-f\]+00.*:${byte}${byte}${byte}" \
> +            "examine 3 bytes forward from ${address_zero}"
> +        gdb_test "x/-6x" \
> +            "0x\[0-9a-f\]+fd.*:${byte}${byte}${byte}${byte}${byte}${byte}" \
> +            "examine 6 bytes backward"
> +        gdb_test "x/-3x ${address_zero}" \
> +            "0x\[0-9a-f\]+fd.*:${byte}${byte}${byte}" \
> +            "examine 3 bytes backward from ${address_zero}"
> +    }
> +}
> +
> +gdb_test_no_output "set charset ASCII"
> +
> +with_test_prefix "char-width=1, print-max=20" {
> +    gdb_test_no_output "set print elements 20"
> +    gdb_test_sequence "x/6s &TestStrings" "take 6 strings forward" {
> +        "\"ABCDEFGHIJKLMNOPQRST\"\.\.\."
> +        "\"UVWXYZ\""
> +        "\"\""
> +        "\"\""
> +        "\"[^\"]+\""
> +        "\"01234567890123456789\"\.\.\."
> +    }
> +    gdb_test "x/-1xb" "0x39" "take 1 char backward"
> +    gdb_test_sequence "x/-6s" "take 6 strings backward" {
> +        "\"ABCDEFGHIJKLMNOPQRST\"\.\.\."
> +        "\"UVWXYZ\""
> +        "\"\""
> +        "\"\""
> +        "\"[^\"]+\""
> +        "\"01234567890123456789\"\.\.\."
> +    }
> +    gdb_test_sequence "x/6s &TestStrings" "take 6 strings forward again" {
> +        "\"ABCDEFGHIJKLMNOPQRST\"\.\.\."
> +        "\"UVWXYZ\""
> +        "\"\""
> +        "\"\""
> +        "\"[^\"]+\""
> +        "\"01234567890123456789\"\.\.\."
> +    }
> +    gdb_test "x/-xb" "0x39" "take 1 char backward again"
> +    gdb_test "x/-s" "\"01234567890123456789\"\.\.\." \
> +        "take 1 string backward (1/6)"
> +    gdb_test "x/-s" "\".+\"" \
> +        "take 1 string backward (2/6)"
> +    gdb_test "x/-s" "\"\"" \
> +        "take 1 string backward (3/6)"
> +    gdb_test "x/-s" "\"\"" \
> +        "take 1 string backward (4/6)"
> +    gdb_test "x/-s" "\"GHIJKLMNOPQRSTUVWXYZ\"" \
> +        "take 1 string backward (5/6)"
> +    gdb_test "x/-s" "\"ABCDEFGHIJKLMNOPQRST\"\.\.\." \
> +        "take 1 string backward (6/6)"
> +}
> +
> +with_test_prefix "char-width=2, print-max=20" {
> +    gdb_test_no_output "set print elements 20"
> +    gdb_test_sequence "x/6sh &TestStringsH" "take 6 strings forward" {
> +        "u\"ABCDEFGHIJKLMNOPQRST\"\.\.\."
> +        "u\"UVWXYZ\""
> +        "u\"\""
> +        "u\"\""
> +        "u\"[^\"]+\""
> +        "u\"01234567890123456789\"\.\.\."
> +    }
> +    gdb_test "x/-1xh" "0x0039" "take 1 char backward"
> +    gdb_test_sequence "x/-6sh" "take 6 strings backward" {
> +        "u\"ABCDEFGHIJKLMNOPQRST\"\.\.\."
> +        "u\"UVWXYZ\""
> +        "u\"\""
> +        "u\"\""
> +        "u\"[^\"]+\""
> +        "u\"01234567890123456789\"\.\.\."
> +    }
> +    gdb_test_sequence "x/6sh &TestStringsH" "take 6 strings forward again" {
> +        "u\"ABCDEFGHIJKLMNOPQRST\"\.\.\."
> +        "u\"UVWXYZ\""
> +        "u\"\""
> +        "u\"\""
> +        "u\"[^\"]+\""
> +        "u\"01234567890123456789\"\.\.\."
> +    }
> +    gdb_test "x/-xh" "0x0039" "take 1 char backward again"
> +    gdb_test "x/-sh" "u\"01234567890123456789\"\.\.\." \
> +        "take 1 string backward (1/6)"
> +    gdb_test "x/-sh" "u\".+\"" \
> +        "take 1 string backward (2/6)"
> +    gdb_test "x/-sh" "u\"\"" \
> +        "take 1 string backward (3/6)"
> +    gdb_test "x/-sh" "u\"\"" \
> +        "take 1 string backward (4/6)"
> +    gdb_test "x/-sh" "u\"GHIJKLMNOPQRSTUVWXYZ\"" \
> +        "take 1 string backward (5/6)"
> +    gdb_test "x/-sh" "u\"ABCDEFGHIJKLMNOPQRST\"\.\.\." \
> +        "take 1 string backward (6/6)"
> +}
> +
> +with_test_prefix "char-width=4, print-max=20" {
> +    gdb_test_no_output "set print elements 20"
> +    gdb_test_sequence "x/6sw &TestStringsW" "take 6 strings forward" {
> +        "U\"ABCDEFGHIJKLMNOPQRST\"\.\.\."
> +        "U\"UVWXYZ\""
> +        "U\"\""
> +        "U\"\""
> +        "U\"[^\"]+\""
> +        "U\"01234567890123456789\"\.\.\."
> +    }
> +    gdb_test "x/-1xw" "0x00000039" "take 1 char backward"
> +    gdb_test_sequence "x/-6sw" "take 6 strings backward" {
> +        "U\"ABCDEFGHIJKLMNOPQRST\"\.\.\."
> +        "U\"UVWXYZ\""
> +        "U\"\""
> +        "U\"\""
> +        "U\"[^\"]+\""
> +        "U\"01234567890123456789\"\.\.\."
> +    }
> +    gdb_test_sequence "x/6sw &TestStringsW" "take 6 strings forward again" {
> +        "U\"ABCDEFGHIJKLMNOPQRST\"\.\.\."
> +        "U\"UVWXYZ\""
> +        "U\"\""
> +        "U\"\""
> +        "U\"[^\"]+\""
> +        "U\"01234567890123456789\"\.\.\."
> +    }
> +    gdb_test "x/-xw" "0x00000039" "take 1 char backward again"
> +    gdb_test "x/-sw" "U\"01234567890123456789\"\.\.\." \
> +        "take 1 string backward (1/6)"
> +    gdb_test "x/-sw" "U\".+\"" \
> +        "take 1 string backward (2/6)"
> +    gdb_test "x/-sw" "U\"\"" \
> +        "take 1 string backward (3/6)"
> +    gdb_test "x/-sw" "U\"\"" \
> +        "take 1 string backward (4/6)"
> +    gdb_test "x/-sw" "U\"GHIJKLMNOPQRSTUVWXYZ\"" \
> +        "take 1 string backward (5/6)"
> +    gdb_test "x/-sw" "U\"ABCDEFGHIJKLMNOPQRST\"\.\.\." \
> +        "take 1 string backward (6/6)"
> +}
> +
> +with_test_prefix "char-width=2, print-max=0" {
> +    gdb_test_no_output "set print elements 0"
> +    gdb_test_sequence "x/6sh &TestStringsH" "take 6 strings forward" {
> +        "u\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\""
> +        "u\"\""
> +        "u\"\""
> +        "u\"\\\\x307b\\\\x3052\\\\x307b\\\\x3052\""
> +        "u\"012345678901234567890123456789\""
> +        "u\"!!!!!!\""
> +    }
> +    gdb_test "x/-4xh" "0x0021\[\t \]+0x0021\[\t \]+0x0021\[\t \]+0x0000" \
> +        "take 4 characters backward"
> +    gdb_test_sequence "x/-6sh" "take 6 strings backward" {
> +        "u\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\""
> +        "u\"\""
> +        "u\"\""
> +        "u\"[^\"]+\""
> +        "u\"012345678901234567890123456789\""
> +        "u\"!!!!!!\""
> +    }
> +    gdb_test_sequence "x/6sh &TestStringsH" "take 6 strings forward again" {
> +        "u\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\""
> +        "u\"\""
> +        "u\"\""
> +        "u\"\\\\x307b\\\\x3052\\\\x307b\\\\x3052\""
> +        "u\"012345678901234567890123456789\""
> +        "u\"!!!!!!\""
> +    }
> +    gdb_test "x/-xh" "0x0000" "take 1 char backward"
> +    gdb_test "x/-sh" "u\"!!!!!!\"" \
> +        "take 1 string backward (1/6)"
> +    gdb_test "x/-sh" "u\"012345678901234567890123456789\"" \
> +        "take 1 string backward (2/6)"
> +    gdb_test "x/-sh" "u\".+\"" \
> +        "take 1 string backward (3/6)"
> +    gdb_test "x/-sh" "u\"\"" \
> +        "take 1 string backward (4/6)"
> +    gdb_test "x/-sh" "u\"\"" \
> +        "take 1 string backward (5/6)"
> +    gdb_test "x/-sh" "u\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"" \
> +        "take 1 string backward (6/6)"
> +}
> +
> +with_test_prefix "char-width=1, print-max=4" {
> +    gdb_test_no_output "set print elements 4"
> +    gdb_test_sequence "x/9s &TestStrings" "take 9 strings forward" {
> +        "\"ABCD\"\.\.\."
> +        "\"EFGH\"\.\.\."
> +        "\"IJKL\"\.\.\."
> +        "\"MNOP\"\.\.\."
> +        "\"QRST\"\.\.\."
> +        "\"UVWX\"\.\.\."
> +        "\"YZ\""
> +        "\"\""
> +        "\"\""
> +    }
> +    gdb_test "x/-xb" "0x00" "take 1 byte backward"
> +    gdb_test_sequence "x/-4s" "take 4 strings backward (1/2)" {
> +        "\"TUVW\"\.\.\."
> +        "\"XYZ\""
> +        "\"\""
> +        "\"\""
> +    }
> +    gdb_test_sequence "x/-4s" "take 4 strings backward (2/2)" {
> +        "\"CDEF\"\.\.\."
> +        "\"GHIJ\"\.\.\."
> +        "\"KLMN\"\.\.\."
> +        "\"OPQR\"\.\.\."
> +    }
> +}
> +
> +with_test_prefix "backward disassemble general" {
> +    set length_to_examine {1 2 3 4 10}
> +    set disassmbly {}
> +
> +    gdb_test "x/i main" "0x\[0-9a-fA-F\]+ <main>:\t.*" \
> +        "move the current position to main (x/i)"
> +    gdb_test "x/-i" "0x\[0-9a-fA-F\]+ <main>:\t.*" \
> +        "move the current position to main (x/-i)"
> +    for {set i 0} {$i < [llength $length_to_examine]} {incr i} {
> +        set len [lindex $length_to_examine $i]
> +        set instructions [capture_command_output "x/${len}i" ""]
> +        lappend disassmbly $instructions
> +    }
> +    for {set i 0} {$i < [llength $length_to_examine]} {incr i} {
> +        set idx [expr [llength $length_to_examine] - $i - 1]
> +        set len [lindex $length_to_examine $idx]
> +        set actual [capture_command_output "x/-${len}i" ""]
> +        set expected [lindex $disassmbly $idx]
> +        if {$actual == $expected} {
> +            pass "inst:$idx"
> +        } else {
> +            fail "inst:$idx"
> +        }
> +    }
> +}



More information about the Gdb-patches mailing list