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]

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


I'm starting a v2 thread for this patch as it seems that I missed
GDB 7.11 release.  I've already finished the copyright process with
FSF.  Please let me know there are any action items on my side.

The previous threads can be found from the links below.
https://sourceware.org/ml/gdb-patches/2016-02/msg00187.html
https://sourceware.org/ml/gdb-patches/2016-03/msg00460.html

With this patch, we can specify a negative repeat count for
all the formats of the 'x' command to examine memory backward.

Tested on i386-linux and amd64-linux. A new testcase 'examine-backward'
was added under gdb.base.  It mainly focuses on the format 'x/-#s'
and 'x/-#i'.  For disassembler testing, the new testcase covers only i386
and amd64, which are skipped on other platforms.

Here's the example output:
(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        # 0x40432e
   0x4041f0 <main(int, char**)+22>: mov    $0x2a,%edi
   0x4041f5 <main(int, char**)+27>: callq  0x404147 <Func1(int, char const*)>
(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.
	(null_fprintf): New function.
	(null_print_address): New function.
	(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                              | 259 +++++++++++++++++++++++++++-
 gdb/testsuite/gdb.base/examine-backward.c   | 143 +++++++++++++++
 gdb/testsuite/gdb.base/examine-backward.exp | 219 +++++++++++++++++++++++
 5 files changed, 646 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 39d99b7..b83c2cd 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  0x0000000000400580 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*)>
+
 * GDB now supports multibit bitfields and enums in target register
   descriptions.
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 7abd55e..da3efc3 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 absolute value of the given number of null-terminated strings or
+instructions before the address are displayed.  For the @samp{i} format,
+we use line number information in the debug info to resolve a correct frame
+while dissasembling backward.  If line info is not available, the command
+stops examining memory with the 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..e0ffcc0 100644
--- a/gdb/printcmd.c
+++ b/gdb/printcmd.c
@@ -47,6 +47,7 @@
 #include "cli/cli-utils.h"
 #include "format.h"
 #include "source.h"
+#include "linespec.h"
 
 #ifdef TUI
 #include "tui/tui.h"		/* For tui_active et al.   */
@@ -186,8 +187,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 +791,221 @@ print_address_demangle (const struct value_print_options *opts,
 }
 
 
+/* These empty functions are used in find_instruction_backward to
+   suppress standard output during the call to gdbarch_print_insn.  */
+
+static int
+null_fprintf (void *stream, const char *format ATTRIBUTE_UNUSED, ...)
+{
+  return 0;
+}
+
+static void
+null_print_address (bfd_vma addr, struct disassemble_info *info)
+{
+  return;
+}
+
+/* Rewind assembly from ADDR and return the start address after the given
+   number of lines are disassembled. To avoid disassembling in a wrong frame,
+   we get addresses in a correct frame using line information.
+   If we go out of the symbol range during disassembling, we return
+   the smallest address we've got so far and set the number of lines read
+   to lines_read.  */
+
+static CORE_ADDR
+find_instruction_backward (struct gdbarch *gdbarch, CORE_ADDR addr,
+          int count, int *lines_read)
+{
+  char addrstr[64];
+  struct symtabs_and_lines sals;
+  CORE_ADDR start_pc, end_pc, ret;
+
+  start_pc = end_pc = 0;
+  ret = addr;
+  *lines_read = 0;
+
+  /* Get the start address of a line which includes the address (addr - 1).  */
+  sprintf (addrstr, "*%p", (void *) (addr - 1));
+  sals = decode_line_with_last_displayed (addrstr, DECODE_LINE_FUNFIRSTLINE);
+  if (sals.nelts >= 1)
+    {
+      if ((!sals.sals[0].line)
+          || (!find_line_pc_range (sals.sals[0], &start_pc, &end_pc)))
+        {
+          printf_filtered (_("No line number information available "
+            "for address "));
+          wrap_here ("  ");
+          print_address (gdbarch, addr - 1, gdb_stdout);
+          printf_filtered ("\n");
+          start_pc = end_pc = 0;
+        }
+      xfree (sals.sals);
+    }
+
+  if (start_pc)
+    {
+      VEC (CORE_ADDR) *pcs = NULL;
+      struct disassemble_info di;
+      CORE_ADDR p;
+      int i;
+
+      VEC_reserve (CORE_ADDR, pcs, count);
+
+      di = gdb_disassemble_info (gdbarch, NULL);
+      di.fprintf_func = null_fprintf;
+      di.print_address_func = null_print_address;
+
+      /* Record each instruction address from START_PC to ADDR.  */
+      p = start_pc;
+      for (i = 0; p < addr; ++i)
+        {
+          VEC_safe_push (CORE_ADDR, pcs, p);
+          p += gdbarch_print_insn (gdbarch, (CORE_ADDR) p, &di);
+        }
+
+      /* If we reach COUNT instructions, return the address recorded in
+         VECTOR. Otherwise, make a recursive call to disassemble
+         the previoius line.  */
+      if (i >= count)
+        {
+          ret = VEC_index (CORE_ADDR, pcs, i - count);
+          *lines_read = count;
+        }
+        else
+        {
+          int linesread_internal = 0;
+          ret = find_instruction_backward (gdbarch,
+               VEC_index (CORE_ADDR, pcs, 0), count - i,
+               &linesread_internal);
+          *lines_read = i + linesread_internal;
+
+          /* Return the smallest valid address we've got
+             if the recursive call above failed.  */
+          if (!ret)
+            ret = VEC_index (CORE_ADDR, pcs, 0);
+        }
+      VEC_free (CORE_ADDR, pcs);
+    }
+  return ret;
+}
+
+/* This is backward version of partial_memory_read in valprint.c.
+   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 an error, the last read was unsuccessful, so adjust count.  */
+      if (errcode != 0)
+        {
+          --nread;
+          printf_filtered (_("Cannot access memory at address "));
+          wrap_here ("  ");
+          print_address (gdbarch, memaddr, gdb_stdout);
+          printf_filtered ("\n");
+        }
+    }
+  return (nread);
+}
+
+/* This is integer version of decimal_is_zero in dfp.c.
+   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 PRINT_MAX of
+   characters 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;
+  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);
+  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) / 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;
+            }
+        }
+    }
+  xfree(buffer);
+
+  /* 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);
+    }
+
+  return string_start_addr;
+}
+
 /* Examine data at address ADDR in format FMT.
    Fetch it from memory and print on gdb_stdout.  */
 
@@ -798,6 +1019,7 @@ do_examine (struct format_data fmt, struct gdbarch *gdbarch, CORE_ADDR addr)
   int i;
   int maxelts;
   struct value_print_options opts;
+  CORE_ADDR addr_rewound = 0;
 
   format = fmt.format;
   size = fmt.size;
@@ -868,6 +1090,35 @@ 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
+         and update next_address with it before exiting the function.  */
+      addr_rewound = (format == 's') ?
+        (next_address - val_type->length) : next_address;
+    }
+
   /* 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 +1164,9 @@ do_examine (struct format_data fmt, struct gdbarch *gdbarch, CORE_ADDR addr)
       printf_filtered ("\n");
       gdb_flush (gdb_stdout);
     }
+
+  if (addr_rewound)
+    next_address = addr_rewound;
 }
 
 static void
@@ -2522,7 +2776,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..3ce77d8
--- /dev/null
+++ b/gdb/testsuite/gdb.base/examine-backward.c
@@ -0,0 +1,143 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2015 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 error 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() {
+
+#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
+};
+
+int main () {
+    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..a3ee008
--- /dev/null
+++ b/gdb/testsuite/gdb.base/examine-backward.exp
@@ -0,0 +1,219 @@
+# Copyright 2015 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/>.
+
+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)
+    }
+    timeout {
+        fail "Fail to get process address spaces (timeout)."
+    }
+    }
+    return ${addr}
+}
+
+# invalid format (# of tests = 5)
+
+gdb_test "x/- 10xb main" "Invalid number \"10xb\"\."
+gdb_test "x/--10xb main" "Invalid number \"10xb\"\."
+gdb_test "x/-a10xb main" "Invalid number \"10xb\"\."
+gdb_test_no_output "x/-0i main"
+gdb_test_no_output "x/-0sh main"
+
+# memory page boundary test (# of tests = 5)
+
+gdb_test_no_output "set print elements 0"
+set boundary [get_first_mapped_address]
+gdb_test "x/3s ${boundary}"
+gdb_test_sequence "x/-4s" "memory page boundary test" {
+    "Cannot access memory at address 0x"
+    "0x"
+    "0x"
+    "0x"
+}
+gdb_test "x/3s ${boundary}"
+gdb_test_sequence "x/-3s" "memory page boundary test" {
+    "Cannot access memory at address 0x"
+    "0x"
+    "0x"
+    "0x"
+}
+
+# char-width=1, print-max=20 (# of tests = 12)
+
+gdb_test_no_output "set print elements 20"
+gdb_test "x/6s &TestStrings"
+gdb_test "x/-1xb" "0x39"
+gdb_test_sequence "x/-6s" "examine multiple strings backward" {
+    "\"ABCDEFGHIJKLMNOPQRST\"\.\.\."
+    "\"UVWXYZ\""
+    "\"\""
+    "\"\""
+    "\"[^\"]+\""
+    "\"01234567890123456789\"\.\.\."
+}
+gdb_test "x/6s &TestStrings"
+gdb_test "x/-xb" "0x39"
+gdb_test "x/-s" "\"01234567890123456789\"\.\.\."
+gdb_test "x/-s" "\".+\""
+gdb_test "x/-s" "\"\""
+gdb_test "x/-s" "\"\""
+gdb_test "x/-s" "\"GHIJKLMNOPQRSTUVWXYZ\""
+gdb_test "x/-s" "\"ABCDEFGHIJKLMNOPQRST\"\.\.\."
+
+# char-width=2, print-max=20 (# of tests = 12)
+
+gdb_test_no_output "set print elements 20"
+gdb_test "x/6sh &TestStringsH"
+gdb_test "x/-1xh" "0x0039"
+gdb_test_sequence "x/-6sh" "examine multiple strings backward" {
+    "u\"ABCDEFGHIJKLMNOPQRST\"\.\.\."
+    "u\"UVWXYZ\""
+    "u\"\""
+    "u\"\""
+    "u\"[^\"]+\""
+    "u\"01234567890123456789\"\.\.\."
+}
+gdb_test "x/6sh &TestStringsH"
+gdb_test "x/-xh" "0x0039"
+gdb_test "x/-sh" "u\"01234567890123456789\"\.\.\."
+gdb_test "x/-sh" "u\".+\""
+gdb_test "x/-sh" "u\"\""
+gdb_test "x/-sh" "u\"\""
+gdb_test "x/-sh" "u\"GHIJKLMNOPQRSTUVWXYZ\""
+gdb_test "x/-sh" "u\"ABCDEFGHIJKLMNOPQRST\"\.\.\."
+
+# char-width=4, print-max=20 (# of tests = 12)
+
+gdb_test_no_output "set print elements 20"
+gdb_test "x/6sw &TestStringsW"
+gdb_test "x/-1xw" "0x00000039"
+gdb_test_sequence "x/-6sw" "examine multiple strings backward" {
+    "U\"ABCDEFGHIJKLMNOPQRST\"\.\.\."
+    "U\"UVWXYZ\""
+    "U\"\""
+    "U\"\""
+    "U\"[^\"]+\""
+    "U\"01234567890123456789\"\.\.\."
+}
+gdb_test "x/6sw &TestStringsW"
+gdb_test "x/-xw" "0x00000039"
+gdb_test "x/-sw" "U\"01234567890123456789\"\.\.\."
+gdb_test "x/-sw" "U\".+\""
+gdb_test "x/-sw" "U\"\""
+gdb_test "x/-sw" "U\"\""
+gdb_test "x/-sw" "U\"GHIJKLMNOPQRSTUVWXYZ\""
+gdb_test "x/-sw" "U\"ABCDEFGHIJKLMNOPQRST\"\.\.\."
+
+# char-width=2, print-max=0 (# of tests = 12)
+
+gdb_test_no_output "set print elements 0"
+gdb_test "x/6sh &TestStringsH"
+gdb_test "x/-4xh" "0x0021\[\t \]+0x0021\[\t \]+0x0021\[\t \]+0x0000"
+gdb_test_sequence "x/-6sh" "examine multiple strings backward" {
+    "u\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\""
+    "u\"\""
+    "u\"\""
+    "u\"[^\"]+\""
+    "u\"012345678901234567890123456789\""
+    "u\"!!!!!!\""
+}
+gdb_test "x/6sh &TestStringsH"
+gdb_test "x/-xh"
+gdb_test "x/-sh" "u\"!!!!!!\""
+gdb_test "x/-sh" "u\"012345678901234567890123456789\""
+gdb_test "x/-sh" "u\".+\""
+gdb_test "x/-sh" "u\"\""
+gdb_test "x/-sh" "u\"\""
+gdb_test "x/-sh" "u\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\""
+
+# char-width=1, print-max=4 (# of tests = 5)
+
+gdb_test_no_output "set print elements 4"
+gdb_test "x/9s &TestStrings"
+gdb_test "x/-xb" "0x00"
+gdb_test_sequence "x/-4s" "examine multiple strings backward" {
+    "\"TUVW\"\.\.\."
+    "\"XYZ\""
+    "\"\""
+    "\"\""
+}
+gdb_test_sequence "x/-4s" "examine multiple strings backward" {
+    "\"CDEF\"\.\.\."
+    "\"GHIJ\"\.\.\."
+    "\"KLMN\"\.\.\."
+    "\"OPQR\"\.\.\."
+}
+
+# Disassemle backward test (# of tests = 10)
+
+gdb_test_no_output "set print elements 200"
+if { [istarget x86_64-*-* ] } {
+    gdb_test "x/20i TestFunction"
+    gdb_test "x/-1i"
+    gdb_test "x/-19i"
+    gdb_test "x/i" "push\[\t \]+%rbp"
+    gdb_test "x/17i TestFunction"
+    gdb_test "x/-2xb"
+    gdb_test_sequence "x/-6i" "examine multiple instructions backward" {
+        "mov\[\t \]+0x8\\(%rdx\\),%rdx\[\t \]*"
+        "vmovsd\[\t \]+\\(%rdx\\),%xmm2\[\t \]*"
+        "mov\[\t \]+-0x4\\(%rbp\\),%edx\[\t \]*"
+        "add\[\t \]+\\$0x1,%edx\[\t \]*"
+        "vxorpd\[\t \]+%xmm0,%xmm0,%xmm0\[\t \]*"
+        "vcvtsi2sd\[\t \]+%edx,%xmm0,%xmm0\[\t \]*"
+    }
+    gdb_test "x/2i TestFunction"
+    gdb_test_sequence "x/-3i" "examine multiple instructions backward" {
+        "No line number information available for address"
+        "push\[\t \]+%rbp"
+        "mov\[\t \]+%rsp\,%rbp"
+    }
+} elseif { [istarget i\[34567\]86-*-*] } {
+    gdb_test "x/20i TestFunction"
+    gdb_test "x/-1i"
+    gdb_test "x/-19i"
+    gdb_test "x/i" "push\[\t \]+%ebp"
+    gdb_test "x/18i TestFunction"
+    gdb_test "x/-2xb"
+    gdb_test_sequence "x/-6i" "examine multiple instructions backward" {
+        "mov\[\t \]+0x4\\(%ecx\\),%ecx\[\t \]*"
+        "fldl\[\t \]+\\(%ecx\\)\[\t \]*"
+        "mov\[\t \]+-0x4\\(%ebp\\),%ecx\[\t \]*"
+        "add\[\t \]+\\$0x1,%ecx\[\t \]*"
+        "mov\[\t \]+%ecx,-0x14\\(%ebp\\)\[\t \]*"
+        "fildl\[\t \]+-0x14\\(%ebp\\)\[\t \]*"
+    }
+    gdb_test "x/2i TestFunction"
+    gdb_test_sequence "x/-3i" "examine multiple instructions backward" {
+        "No line number information available for address"
+        "push\[\t \]+%ebp"
+        "mov\[\t \]+%esp\,%ebp"
+    }
+}
-- 
2.5.0


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