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


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

Re: [PATCH V3] Add negative repeat count to 'x' command


Hi Toshihito,

Sorry for the delay.  I'm been swamped with other things,
and needed to reserve a slot to grok this all.  I think this
is getting close.  One more iteration and this is probably
good to go.

Below I'll mostly point out what I stumbled on trying to
understand the code, and what I think could be improved to
make it easier for the next person, or our future selves,
understand this.  Hope you find it constructive.

> This change adds support for specifying a negative repeat count to
> all the formats of the 'x' command to examine memory backward.
> 
> A new testsuite 'examine-backward' is added to cover this new feature.
> It mainly focuses on the format 'x/-#s' and 'x/-#i'.  For disassemble
> testing, only i386 and amd64 are covered.  Other platforms are skipped.

This comment is no longer up to date, right?

I see you still left the x86/x64-64-specific tests in place.  Now that
we have the generic disassembly test, is there value in keeping them?
I'm thinking that they're probably making the test fail on e.g., x32.

> 
> Here's the example output from a 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.
> 
> 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                              | 249 +++++++++++++-
>  gdb/testsuite/gdb.base/examine-backward.c   | 167 +++++++++
>  gdb/testsuite/gdb.base/examine-backward.exp | 502 ++++++++++++++++++++++++++++
>  5 files changed, 943 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 7bf1e1a..a9faf78 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 f74c41c..9decec4 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
> +procedure 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..60592e5 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,208 @@ print_address_demangle (const struct value_print_options *opts,
>  }
>  
>  
> +/* Rewind assembly from ADDR and return the start address after COUNT lines
> +   are disassembled. 

FWIW, I found this sentence incredibly confusing.  What's "rewind"?  
And, the START address AFTER lines are disassembled?  What's that?
If you've already disassembled, how can it be the start?

I'd find this clearer:

/* Find the address of the instruction that is COUNT instructions
   before the instruction at ADDR.  */ 


>  We use line number information to accurately locate
> +   procedure boundaries.

I'd say:

  Since some architectures have variable-length instructions, we 
  can't just simply subtract 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 lines read to LINES_READ.  */

Is it really _lines_ read though?  It took me a while to grok this all, but I
think it's actually number of instructions we were able to count/read backwards?

> +
> +static CORE_ADDR
> +find_instruction_backward (struct gdbarch *gdbarch, CORE_ADDR addr,
> +                           int count, int *lines_read)

A variable named "count" in this function is confusing, since it's
it's not immediately clear whether it counts instructions, or lines.
insn_count / line_count ?  But, see above.  Actually, having now
read the code and come back here, I think it's "lines_read" that is
the most misleading parameter name.


> +{
> +  /* The vector PCS is used to store procedure boundary addressese within
> +     a pc range.  */

typo "addressese".

> +  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);
> +
> +  *lines_read = 0;
> +  loop_start = loop_end = addr;
> +
> +  /* In each iteration of the outer loop, we get a pc range which ends before
> +     LOOP_START, then we count and store each procedure boundary from the start
> +     address of the range to the end.

I don't think we store "store each procedure boundary".  Seems like what
we store in the VEC is every instruction address of the range currently
iterated?

> +     If the number of instructions counted reaches COUNT, return one of stored
> +     addresses which is located COUNT instructions back from ADDR.
> +     If COUNT is not reached, we subtract the number of counted instructions
> +     from 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 procedure boundaries in a 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);
> +        }
> +
> +      count -= VEC_length (CORE_ADDR, pcs);
> +      *lines_read += VEC_length (CORE_ADDR, pcs);

How come the VEC's length, which holds instruction addresses,
is added to lines_read?  Shouldn't this be just incrementing
lines_read by one, or some such?  OK, I think the problem
is actually that you're calling this "lines", maybe thinking
of disassembly-lines-displayed, which is confusing with
"source lines" / sals.   I think you should rename this to
something else.  

/me goes update the comments above.

I wonder if this algoritm will still work with optimized code
and inlined functions, and PCs / lines jumping around.

> +    }
> +  while (count > 0);
> +
> +  /* After the loop, the vector PCS has procedure boundaries in the last
> +     range we processed, and COUNT has a negative value.  We return an address
> +     at the index of -COUNT in the vector for the reason below.
> +     Assuming that the processed ranges are {4000, 4001, 4005},

So IIRC, "{4000, 4001, 4005}" is the addresses covered by a line, not really
a range, right?  A range with 3 elements would be odd.

>  {4009, 400c},
> +     and the original COUNT is 4, we want to return 4001.  When we reach here,
> +     COUNT is set to -1 because it is subtracted by 2 and 3.  The value 4001
> +     is located at the index 1 of the last range, which can be calculated by
> +     VEC_length - (COUNT + VEC_length) = -COUNT.
> +     The case when the length of PCS is 0 means that we reach an area where
> +     line info is not available.  In such a case, we return LOOP_START which
> +     is the lowest procedure boundary with line info.  */
> +  p = VEC_length (CORE_ADDR, pcs) > 0
> +    ? VEC_index (CORE_ADDR, pcs, -count)
> +    : loop_start;
> +
> +  /* LINES_READ includes entire ranges.  Need to exclude a beginning part,
> +     that is {4000} in the example above.  */
> +  if (count < 0)
> +    *lines_read += count;
> +
> +  do_cleanups (cleanup);
> +  return p;
> +}
> +
> +/* This is a backward version of partial_memory_read in valprint.c.

Should we put this next to partial_memory_read ?

Thought OTOH, both should probably be moved to target.c, next to
read_memory_robust or some such.  (partial_memory_read could probably
be eliminated).

> +   Read LEN bytes of target memory at address MEMADDR, placing the
> +   results in GDB's memory at MYADDR.  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.  */
> +      for (errcode = 0, nread = 0, memaddr += len, myaddr += len;
> +           len > 0 && errcode == 0;
> +           ++nread, --len)
> +        {
> +          errcode = target_read_memory (--memaddr, --myaddr, 1);
> +        }
> +      if (errcode != 0)
> +        {
> +          --nread;
> +          printf_filtered (_("Cannot access memory at address %s\n"),
> +                           paddress (gdbarch, memaddr));
> +        }

I find the putting initialization of non-iterator variables 
in the 'for' initializer statement to be a lot more confusing than
necessary.  Likewise for the errcode condition and the need
to rewind nread.  I'd rather rewrite it like this:

      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 adjust count.  */
              printf_filtered (_("Cannot access memory at address %s\n"),
                               paddress (gdbarch, memaddr));
              break;
            }
        }

Seems simpler and much clearer to me.

Also, we should probably be taking into account the addressable
unit size here.

> +    }
> +  return nread;
> +}
> +
> +/* This is an integer version of decimal_is_zero in dfp.c.

The implementation is completely different.  I'd just drop this
comment.

> +   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 which
> +         is 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 +1005,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 +1077,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,
> +                                               val_type->length,
> +                                               &opts, &count);
> +        }
> +      else
> +        {
> +          next_address = addr - count * val_type->length;
> +        }
> +
> +      /* The following call to print_formatted updates next_address in every
> +         iteration. In backward case, we store the start address here

Double-space after period.

> +         and update next_address with it before exiting the function.  */
> +      addr_rewound = (format == 's'
> +                      ? next_address - val_type->length
> +                      : 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 +1154,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 +2766,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..cdf2c44
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/examine-backward.c
> @@ -0,0 +1,167 @@
> +/* 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/>.  */
> +
> +#include <sys/mman.h>
> +
> +/*
> +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
> +};
> +
> +static __attribute__((noinline)) void
> +TestFunction (void)
> +{
> +#if (defined __x86_64__)
> +  asm ("mov    %rdi,-0x18(%rbp)");
> +  asm ("movl   $0x0,-0x4(%rbp)");
> +  asm ("label1:");
> +  asm ("mov    -0x18(%rbp),%rax");
> +  asm ("mov    0x10(%rax),%eax");
> +  asm ("cmp    -0x4(%rbp),%eax");
> +  asm ("jle    label2");
> +  asm ("mov    -0x18(%rbp),%rax");
> +  asm ("mov    0x8(%rax),%rax");
> +  asm ("mov    -0x18(%rbp),%rdx");
> +  asm ("mov    0x8(%rdx),%rdx");
> +  asm ("vmovsd (%rdx),%xmm2");
> +  asm ("mov    -0x4(%rbp),%edx");
> +  asm ("add    $0x1,%edx");
> +  asm ("vxorpd %xmm0,%xmm0,%xmm0");
> +  asm ("vcvtsi2sd %edx,%xmm0,%xmm0");
> +  asm ("vmovsd 0x725(%rip),%xmm1");
> +  asm ("vdivsd %xmm0,%xmm1,%xmm0");
> +  asm ("vaddsd %xmm0,%xmm2,%xmm0");
> +  asm ("vmovsd %xmm0,(%rax)");
> +  asm ("addl   $0x1,-0x4(%rbp)");
> +  asm ("jmp    label1");
> +  asm ("label2:");
> +  asm ("nop");
> +#elif (defined __i386__)
> +  asm ("sub    $0x18,%esp");
> +  asm ("add    $0x327b,%edx");
> +  asm ("movl   $0x0,-0x4(%ebp)");
> +  asm ("label1:");
> +  asm ("mov    0x8(%ebp),%eax");
> +  asm ("mov    0x8(%eax),%eax");
> +  asm ("cmp    -0x4(%ebp),%eax");
> +  asm ("jle    label2");
> +  asm ("mov    0x8(%ebp),%eax");
> +  asm ("mov    0x4(%eax),%eax");
> +  asm ("mov    0x8(%ebp),%ecx");
> +  asm ("mov    0x4(%ecx),%ecx");
> +  asm ("fldl   (%ecx)");
> +  asm ("mov    -0x4(%ebp),%ecx");
> +  asm ("add    $0x1,%ecx");
> +  asm ("mov    %ecx,-0x14(%ebp)");
> +  asm ("fildl  -0x14(%ebp)");
> +  asm ("fld1   ");
> +  asm ("fdivp  %st,%st(1)");
> +  asm ("faddp  %st,%st(1)");
> +  asm ("fstpl  (%eax)");
> +  asm ("addl   $0x1,-0x4(%ebp)");
> +  asm ("jmp    label1");
> +  asm ("label2:");
> +  asm ("nop");
> +#endif
> +};
> +
> +static __attribute__((noinline)) void
> +zero_mapping_test ()\
> +{
> +}
> +
> +int main ()

int 
main (void)


> +{
> +    /* To make the address zero valid, we call mmap for addr = 0 with
> +       MAP_FIXED.  This syscall is expected to fail under normal configuration
> +       because that space is protected by CONFIG_DEFAULT_MMAP_MIN_ADDR.
> +       This is for non-MMU platforms.   */

What's the high-level idea behind doing this?  If it's a non-MMU platform,
then I don't think you need this?

It's be nice to have the test not depend on mmap, or make it still work
when mmap is not available, as then it'd expose the test to a bunch of
bare metal ports / architectures gdb supports.

> +    const int page_size = 4096;
> +    void *p = mmap (0, page_size,
> +        PROT_READ|PROT_WRITE,
> +        MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS,
> +        -1, 0);

Formatting.  PROT_READ should align under the 0.

> +
> +    /* Call zero_mapping_test regardless of the result from mmap to make sure
> +       a breakpoint at zero_mapping_test is always hit in a test scenario.  */
> +    zero_mapping_test();

Space before parens.

> +
> +    if (p != MAP_FAILED)
> +        munmap (p, page_size);

Indentation doesn't look right.

> +    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..81ebe58
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/examine-backward.exp
> @@ -0,0 +1,502 @@
> +# 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.  There are a couple of limitations depending on
> +# platforms.
> +#
> +# The section "memory page boundary" is to test examining a boundary between
> +# unmapped/mapped address.  If a platform does not support the command
> +# 'info proc mapping', we skip it.
> +
> +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)
> +        }

Should probably add:

        -re "$gdb_prompt $" {
        }

to avoid a FAIL on targets that don't support "info proc mappings".
Call UNSUPPORTED here or in the caller, so there's something logged
in gdb.sum.

> +    }
> +    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" {
> +    gdb_test "b zero_mapping_test" "Breakpoint.*" \
> +        "set breakpoint at zero_mapping_test"
> +    gdb_test "c" "zero_mapping_test.*" "run until zero_mapping_test"
> +    if {[is_address_zero_readable]} {
> +        gdb_test "x/3x 0" \
> +            "0x\[0-9a-f\]+:\t0x\[0-9a-f\]+\t0x\[0-9a-f\]+\t0x\[0-9a-f\]+" \
> +            "examine 4 bytes forward"
> +        gdb_test "x/-6x" \
> +            "0x\[0-9a-f\]+:\tCannot access memory at address 0x\[0-9a-f\]+" \
> +            "examine 6 bytes backward"
> +    }
> +    gdb_test "x/-10x 0" \
> +        "0x\[0-9a-f\]+:\tCannot access memory at address 0x\[0-9a-f\]+" \
> +        "examine 10 bytes backward from NULL"

So in the last test, the address wraps around.  On a non-MMU this will
actually read memory from a high address.  Is this what we want?  Or do
we want to have GDB to never try to go before 0?  Likewise end of addr space.

Thanks,
Pedro Alves


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