New "find" command.
authorDoug Evans <dje@google.com>
Fri, 9 May 2008 17:02:03 +0000 (17:02 +0000)
committerDoug Evans <dje@google.com>
Fri, 9 May 2008 17:02:03 +0000 (17:02 +0000)
* NEWS: Document find command and qSearch:memory packet.
* Makefile.in (SFILES): Add findcmd.c.
(COMMON_OBJS): Add findcmd.o.
(findcmd.o): New rule.
* findcmd.c: New file.
* target.h (target_ops): New member to_search_memory.
(simple_search_memory): Declare.
(target_search_memory): Declare.
* target.c (simple_search_memory): New fn.
(target_search_memory): New fn.
* remote.c (PACKET_qSearch_memory): New packet kind.
(remote_search_memory): New fn.
(init_remote_ops): Init to_search_memory.
(init_extended_remote_ops): Ditto.
(_initialize_remote): Add qSearch:memory packet config command.

* gdbserver/server.h (decode_search_memory_packet): Declare.
* gdbserver/remote-utils.c (decode_search_memory_packet): New fn.
* gdbserver/server.c (handle_search_memory_1): New fn.
(handle_search_memory): New fn.
(handle_query): Process qSearch:memory packets.

* doc/gdb.texinfo: Document "find" command, qSearch:memory packet.

* testsuite/gdb.base/find.exp: New file.
* testsuite/gdb.base/find.c: New file.

16 files changed:
gdb/ChangeLog
gdb/Makefile.in
gdb/NEWS
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/findcmd.c [new file with mode: 0644]
gdb/gdbserver/ChangeLog
gdb/gdbserver/remote-utils.c
gdb/gdbserver/server.c
gdb/gdbserver/server.h
gdb/remote.c
gdb/target.c
gdb/target.h
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/find.c [new file with mode: 0644]
gdb/testsuite/gdb.base/find.exp [new file with mode: 0644]

index b66f661462f2da21f574f06412f22e5e3005f312..883d5074faab4a590cc09428fd6a525ad2ae932f 100644 (file)
@@ -1,3 +1,22 @@
+2008-05-09  Doug Evans  <dje@google.com>
+
+       New "find" command.
+       * NEWS: Document find command and qSearch:memory packet.
+       * Makefile.in (SFILES): Add findcmd.c.
+       (COMMON_OBJS): Add findcmd.o.
+       (findcmd.o): New rule.
+       * findcmd.c: New file.
+       * target.h (target_ops): New member to_search_memory.
+       (simple_search_memory): Declare.
+       (target_search_memory): Declare.
+       * target.c (simple_search_memory): New fn.
+       (target_search_memory): New fn.
+       * remote.c (PACKET_qSearch_memory): New packet kind.
+       (remote_search_memory): New fn.
+       (init_remote_ops): Init to_search_memory.
+       (init_extended_remote_ops): Ditto.
+       (_initialize_remote): Add qSearch:memory packet config command.
+
 2008-05-09  Eli Zaretskii  <eliz@gnu.org>
 
        * thread.c (_initialize_thread): Don't use commas and periods in
index 75151f6cf303b6053a88e57043cf5e2098f93da6..c4b64b85aae56017f5653d20e8f9343aae5723b0 100644 (file)
@@ -609,9 +609,8 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c  \
        dbxread.c demangle.c dictionary.c disasm.c doublest.c dummy-frame.c \
        dwarf2expr.c dwarf2loc.c dwarf2read.c dwarf2-frame.c \
        elfread.c environ.c eval.c event-loop.c event-top.c expprint.c \
-       f-exp.y f-lang.c f-typeprint.c f-valprint.c findvar.c frame.c \
-       frame-base.c \
-       frame-unwind.c \
+       f-exp.y f-lang.c f-typeprint.c f-valprint.c findcmd.c findvar.c \
+       frame.c frame-base.c frame-unwind.c \
        gdbarch.c arch-utils.c gdbtypes.c gnu-v2-abi.c gnu-v3-abi.c \
        inf-loop.c \
        infcall.c \
@@ -1061,6 +1060,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
        event-loop.o event-top.o inf-loop.o completer.o \
        gdbarch.o arch-utils.o gdbtypes.o osabi.o copying.o \
        memattr.o mem-break.o target.o parse.o language.o buildsym.o \
+       findcmd.o \
        std-regs.o \
        signals.o \
        gdb-events.o \
@@ -2136,6 +2136,8 @@ fbsd-nat.o: fbsd-nat.c $(defs_h) $(gdbcore_h) $(inferior_h) $(regcache_h) \
 f-exp.o: f-exp.c $(defs_h) $(gdb_string_h) $(expression_h) $(value_h) \
        $(parser_defs_h) $(language_h) $(f_lang_h) $(bfd_h) $(symfile_h) \
        $(objfiles_h) $(block_h)
+findcmd.o: findcmd.c $(defs_h) $(gdb_string_h) $(gdbcmd_h) $(value_h) \
+       $(target_h)
 findvar.o: findvar.c $(defs_h) $(symtab_h) $(gdbtypes_h) $(frame_h) \
        $(value_h) $(gdbcore_h) $(inferior_h) $(target_h) $(gdb_string_h) \
        $(gdb_assert_h) $(floatformat_h) $(symfile_h) $(regcache_h) \
index 550f96bee0fb92a8591c954dfc905c302aa1c04c..86ce21624ba879bbd5679ffb1d909a0652dc1431 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,11 @@
 
 *** Changes since GDB 6.8
 
+* New remote packets
+
+qSearch:memory:
+  Search memory for a sequence of bytes.
+
 * The "disassemble" command now supports an optional /m modifier to print mixed
 source+assembly.
 
@@ -35,6 +40,10 @@ have also been fixed.
 
 * New commands
 
+find [/size-char] [/max-count] start-address, end-address|+search-space-size,
+    val1 [, val2, ...]
+  Search memory for a sequence of bytes.
+
 set debug timetstamp
 show debug timestamp
   Display timestamps with GDB debugging output.
index dd5ee734099e1025de61c33a8a2a338c7dcf01a9..705a2573c2715d84bf57a779c070fd99bab6494b 100644 (file)
@@ -1,3 +1,7 @@
+2008-05-09  Doug Evans  <dje@google.com>
+
+       * doc/gdb.texinfo: Document "find" command, qSearch:memory packet.
+
 2008-05-05  Doug Evans  <dje@google.com>
 
        * gdb.texinfo (disassemble): Document /m modifier.
index 16b9b66447f4f37e3f8809125e2d2d17c6d4841a..c7fc4ac6a29523862fc2a6de669b4a551fc49518 100644 (file)
@@ -5590,6 +5590,7 @@ Table}.
 * Character Sets::              Debugging programs that use a different
                                 character set than GDB does
 * Caching Remote Data::         Data caching for remote targets
+* Searching Memory::            Searching memory for a sequence of bytes
 @end menu
 
 @node Expressions
@@ -7653,6 +7654,104 @@ state (dirty, bad, ok, etc.).  This command is useful for debugging
 the data cache operation.
 @end table
 
+@node Searching Memory
+@section Search Memory
+@cindex searching memory
+
+Memory can be searched for a particular sequence of bytes with the
+@code{find} command.
+
+@table @code
+@kindex find
+@item find @r{[}/@var{sn}@r{]} @var{start_addr}, +@var{len}, @var{val1} @r{[}, @var{val2}, @dots{}@r{]}
+@itemx find @r{[}/@var{sn}@r{]} @var{start_addr}, @var{end_addr}, @var{val1} @r{[}, @var{val2}, @dots{}@r{]}
+Search memory for the sequence of bytes specified by @var{val1}, @var{val2},
+etc.  The search begins at address @var{start_addr} and continues for either
+@var{len} bytes or through to @var{end_addr} inclusive.
+@end table
+
+@var{s} and @var{n} are optional parameters.
+They may be specified in either order, apart or together.
+
+@table @r
+@item @var{s}, search query size
+The size of each search query value.
+
+@table @code
+@item b
+bytes
+@item h
+halfwords (two bytes)
+@item w
+words (four bytes)
+@item g
+giant words (eight bytes)
+@end table
+
+All values are interpreted in the current language.
+This means, for example, that if the current source language is C/C@t{++}
+then searching for the string ``hello'' includes the trailing '\0'.
+
+If the value size is not specified, it is taken from the
+value's type in the current language.
+This is useful when one wants to specify the search
+pattern as a mixture of types.
+Note that this means, for example, that in the case of C-like languages
+a search for an untyped 0x42 will search for @samp{(int) 0x42}
+which is typically four bytes.
+
+@item @var{n}, maximum number of finds
+The maximum number of matches to print.  The default is to print all finds.
+@end table
+
+You can use strings as search values.  Quote them with double-quotes
+ (@code{"}).
+The string value is copied into the search pattern byte by byte,
+regardless of the endianness of the target and the size specification.
+
+The address of each match found is printed as well as a count of the
+number of matches found.
+
+The address of the last value found is stored in convenience variable
+@samp{$_}.
+A count of the number of matches is stored in @samp{$numfound}.
+
+For example, if stopped at the @code{printf} in this function:
+
+@smallexample
+void
+hello ()
+@{
+  static char hello[] = "hello-hello";
+  static struct @{ char c; short s; int i; @}
+    __attribute__ ((packed)) mixed
+    = @{ 'c', 0x1234, 0x87654321 @};
+  printf ("%s\n", hello);
+@}
+@end smallexample
+
+@noindent
+you get during debugging:
+
+@smallexample
+(gdb) find &hello[0], +sizeof(hello), "hello"
+0x804956d <hello.1620+6>
+1 pattern found
+(gdb) find &hello[0], +sizeof(hello), 'h', 'e', 'l', 'l', 'o'
+0x8049567 <hello.1620>
+0x804956d <hello.1620+6>
+2 patterns found
+(gdb) find /b1 &hello[0], +sizeof(hello), 'h', 0x65, 'l'
+0x8049567 <hello.1620>
+1 pattern found
+(gdb) find &mixed, +sizeof(mixed), (char) 'c', (short) 0x1234, (int) 0x87654321
+0x8049560 <mixed.1625>
+1 pattern found
+(gdb) print $numfound
+$1 = 1
+(gdb) print $_
+$2 = (void *) 0x8049560
+@end smallexample
 
 @node Macros
 @chapter C Preprocessor Macros
@@ -13496,6 +13595,10 @@ are:
 @tab @code{qGetTLSAddr}
 @tab Displaying @code{__thread} variables
 
+@item @code{search-memory}
+@tab @code{qSearch:memory}
+@tab @code{find}
+
 @item @code{supported-packets}
 @tab @code{qSupported}
 @tab Remote communications parameters
@@ -24606,6 +24709,26 @@ command by a @samp{,}, not a @samp{:}, contrary to the naming
 conventions above.  Please don't use this packet as a model for new
 packets.)
 
+@item qSearch:memory:@var{address};@var{length};@var{search-pattern}
+@cindex searching memory, in remote debugging
+@cindex @samp{qSearch:memory} packet
+@anchor{qSearch memory}
+Search @var{length} bytes at @var{address} for @var{search-pattern}.
+@var{address} and @var{length} are encoded in hex.
+@var{search-pattern} is a sequence of bytes, hex encoded.
+
+Reply:
+@table @samp
+@item 0
+The pattern was not found.
+@item 1,address
+The pattern was found at @var{address}.
+@item E @var{NN}
+A badly formed request or an error was encountered while searching memory.
+@item
+An empty reply indicates that @samp{qSearch:memory} is not recognized.
+@end table
+
 @item qSupported @r{[}:@var{gdbfeature} @r{[};@var{gdbfeature}@r{]}@dots{} @r{]}
 @cindex supported packets, remote query
 @cindex features of the remote protocol
diff --git a/gdb/findcmd.c b/gdb/findcmd.c
new file mode 100644 (file)
index 0000000..dfb746b
--- /dev/null
@@ -0,0 +1,331 @@
+/* The find command.
+
+   Copyright (C) 2008 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 "defs.h"
+#include <ctype.h>
+#include "gdb_string.h"
+#include "gdbcmd.h"
+#include "value.h"
+#include "target.h"
+
+/* Copied from bfd_put_bits.  */
+
+static void
+put_bits (bfd_uint64_t data, char *buf, int bits, bfd_boolean big_p)
+{
+  int i;
+  int bytes;
+
+  gdb_assert (bits % 8 == 0);
+
+  bytes = bits / 8;
+  for (i = 0; i < bytes; i++)
+    {
+      int index = big_p ? bytes - i - 1 : i;
+
+      buf[index] = data & 0xff;
+      data >>= 8;
+    }
+}
+
+/* Subroutine of find_command to simplify it.
+   Parse the arguments of the "find" command.  */
+
+static void
+parse_find_args (char *args, ULONGEST *max_countp,
+                char **pattern_bufp, ULONGEST *pattern_lenp,
+                CORE_ADDR *start_addrp, ULONGEST *search_space_lenp)
+{
+  /* Default to using the specified type.  */
+  char size = '\0';
+  ULONGEST max_count = ~(ULONGEST) 0;
+  /* Buffer to hold the search pattern.  */
+  char *pattern_buf;
+  /* Current size of search pattern buffer.
+     We realloc space as needed.  */
+#define INITIAL_PATTERN_BUF_SIZE 100
+  ULONGEST pattern_buf_size = INITIAL_PATTERN_BUF_SIZE;
+  /* Pointer to one past the last in-use part of pattern_buf.  */
+  char *pattern_buf_end;
+  ULONGEST pattern_len;
+  CORE_ADDR start_addr;
+  ULONGEST search_space_len;
+  char *s = args;
+  bfd_boolean big_p = gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_BIG;
+  struct cleanup *old_cleanups;
+  struct value *v;
+
+  if (args == NULL)
+    error (_("missing search parameters"));
+
+  pattern_buf = xmalloc (pattern_buf_size);
+  pattern_buf_end = pattern_buf;
+  old_cleanups = make_cleanup (free_current_contents, &pattern_buf);
+
+  /* Get search granularity and/or max count if specified.
+     They may be specified in either order, together or separately.  */
+
+  while (*s == '/')
+    {
+      ++s;
+
+      while (*s != '\0' && *s != '/' && !isspace (*s))
+       {
+         if (isdigit (*s))
+           {
+             max_count = atoi (s);
+             while (isdigit (*s))
+               ++s;
+             continue;
+           }
+
+         switch (*s)
+           {
+           case 'b':
+           case 'h':
+           case 'w':
+           case 'g':
+             size = *s++;
+             break;
+           default:
+             error (_("invalid size granularity"));
+           }
+       }
+
+      while (isspace (*s))
+       ++s;
+    }
+
+  /* Get the search range.  */
+
+  v = parse_to_comma_and_eval (&s);
+  start_addr = value_as_address (v);
+
+  if (*s == ',')
+    ++s;
+  while (isspace (*s))
+    ++s;
+
+  if (*s == '+')
+    {
+      LONGEST len;
+      ++s;
+      v = parse_to_comma_and_eval (&s);
+      len = value_as_long (v);
+      if (len == 0)
+       {
+         printf_filtered (_("empty search range\n"));
+         return;
+       }
+      if (len < 0)
+       error (_("invalid length"));
+      /* Watch for overflows.  */
+      if (len > CORE_ADDR_MAX
+         || (start_addr + len - 1) < start_addr)
+       error (_("search space too large"));
+      search_space_len = len;
+    }
+  else
+    {
+      CORE_ADDR end_addr;
+      v = parse_to_comma_and_eval (&s);
+      end_addr = value_as_address (v);
+      if (start_addr > end_addr)
+       error (_("invalid search space, end preceeds start"));
+      search_space_len = end_addr - start_addr + 1;
+      /* We don't support searching all of memory
+        (i.e. start=0, end = 0xff..ff).
+        Bail to avoid overflows later on.  */
+      if (search_space_len == 0)
+       error (_("overflow in address range computation, choose smaller range"));
+    }
+
+  if (*s == ',')
+    ++s;
+
+  /* Fetch the search string.  */
+
+  while (*s != '\0')
+    {
+      LONGEST x;
+      int val_bytes;
+
+      while (isspace (*s))
+       ++s;
+
+      v = parse_to_comma_and_eval (&s);
+      val_bytes = TYPE_LENGTH (value_type (v));
+
+      /* Keep it simple and assume size == 'g' when watching for when we
+        need to grow the pattern buf.  */
+      if ((pattern_buf_end - pattern_buf + max (val_bytes, sizeof (int64_t)))
+         > pattern_buf_size)
+       {
+         size_t current_offset = pattern_buf_end - pattern_buf;
+         pattern_buf_size *= 2;
+         pattern_buf = xrealloc (pattern_buf, pattern_buf_size);
+         pattern_buf_end = pattern_buf + current_offset;
+       }
+
+      if (size != '\0')
+       {
+         x = value_as_long (v);
+         switch (size)
+           {
+           case 'b':
+             *pattern_buf_end++ = x;
+             break;
+           case 'h':
+             put_bits (x, pattern_buf_end, 16, big_p);
+             pattern_buf_end += sizeof (int16_t);
+             break;
+           case 'w':
+             put_bits (x, pattern_buf_end, 32, big_p);
+             pattern_buf_end += sizeof (int32_t);
+             break;
+           case 'g':
+             put_bits (x, pattern_buf_end, 64, big_p);
+             pattern_buf_end += sizeof (int64_t);
+             break;
+           }
+       }
+      else
+       {
+         memcpy (pattern_buf_end, value_contents_raw (v), val_bytes);
+         pattern_buf_end += val_bytes;
+       }
+
+      if (*s == ',')
+       ++s;
+      while (isspace (*s))
+       ++s;
+    }
+
+  if (pattern_buf_end == pattern_buf)
+    error (_("missing search pattern"));
+
+  pattern_len = pattern_buf_end - pattern_buf;
+
+  if (search_space_len < pattern_len)
+    error (_("search space too small to contain pattern"));
+
+  *max_countp = max_count;
+  *pattern_bufp = pattern_buf;
+  *pattern_lenp = pattern_len;
+  *start_addrp = start_addr;
+  *search_space_lenp = search_space_len;
+
+  /* We successfully parsed the arguments, leave the freeing of PATTERN_BUF
+     to the caller now.  */
+  discard_cleanups (old_cleanups);
+}
+
+static void
+find_command (char *args, int from_tty)
+{
+  /* Command line parameters.
+     These are initialized to avoid uninitialized warnings from -Wall.  */
+  ULONGEST max_count = 0;
+  char *pattern_buf = 0;
+  ULONGEST pattern_len = 0;
+  CORE_ADDR start_addr = 0;
+  ULONGEST search_space_len = 0;
+  /* End of command line parameters.  */
+  unsigned int found_count;
+  CORE_ADDR last_found_addr;
+  struct cleanup *old_cleanups;
+
+  parse_find_args (args, &max_count, &pattern_buf, &pattern_len, 
+                  &start_addr, &search_space_len);
+
+  old_cleanups = make_cleanup (free_current_contents, &pattern_buf);
+
+  /* Perform the search.  */
+
+  found_count = 0;
+  last_found_addr = 0;
+
+  while (search_space_len >= pattern_len
+        && found_count < max_count)
+    {
+      /* Offset from start of this iteration to the next iteration.  */
+      ULONGEST next_iter_incr;
+      CORE_ADDR found_addr;
+      int found = target_search_memory (start_addr, search_space_len,
+                                       pattern_buf, pattern_len, &found_addr);
+
+      if (found <= 0)
+       break;
+
+      print_address (found_addr, gdb_stdout);
+      printf_filtered ("\n");
+      ++found_count;
+      last_found_addr = found_addr;
+
+      /* Begin next iteration at one byte past this match.  */
+      next_iter_incr = (found_addr - start_addr) + 1;
+
+      /* For robustness, we don't let search_space_len go -ve here.  */
+      if (search_space_len >= next_iter_incr)
+       search_space_len -= next_iter_incr;
+      else
+       search_space_len = 0;
+      start_addr += next_iter_incr;
+    }
+
+  /* Record and print the results.  */
+
+  set_internalvar (lookup_internalvar ("numfound"),
+                  value_from_longest (builtin_type_int,
+                                      (LONGEST) found_count));
+  if (found_count > 0)
+    {
+      set_internalvar (lookup_internalvar ("_"),
+                      value_from_pointer (builtin_type_void_data_ptr,
+                                          last_found_addr));
+    }
+
+  if (found_count == 0)
+    printf_filtered ("pattern not found\n");
+  else
+    printf_filtered ("%d pattern%s found\n", found_count,
+                    found_count > 1 ? "s" : "");
+
+  do_cleanups (old_cleanups);
+}
+
+void
+_initialize_mem_search (void)
+{
+  add_cmd ("find", class_vars, find_command, _("\
+Search memory for a sequence of bytes.\n\
+Usage:\n\
+find [/size-char] [/max-count] start-address, end-address, expr1 [, expr2 ...]\n\
+find [/size-char] [/max-count] start-address, +length, expr1 [, expr2 ...]\n\
+size-char is one of b,h,w,g for 8,16,32,64 bit values respectively,\n\
+and if not specified the size is taken from the type of the expression\n\
+in the current language.\n\
+Note that this means for example that in the case of C-like languages\n\
+a search for an untyped 0x42 will search for \"(int) 0x42\"\n\
+which is typically four bytes.\n\
+\n\
+The address of the last match is stored as the value of \"$_\".\n\
+Convenience variable \"$numfound\" is set to the number of matches."),
+          &cmdlist);
+}
index ab5396443b5cfeaa16fcacabc4850d4a5c1cd2bb..d8e4037f840c037219f320e8085f4200185ed8dc 100644 (file)
@@ -1,3 +1,11 @@
+2008-05-09  Doug Evans  <dje@google.com>
+
+       * gdbserver/server.h (decode_search_memory_packet): Declare.
+       * gdbserver/remote-utils.c (decode_search_memory_packet): New fn.
+       * gdbserver/server.c (handle_search_memory_1): New fn.
+       (handle_search_memory): New fn.
+       (handle_query): Process qSearch:memory packets.
+
 2008-05-08  Ulrich Weigand  <uweigand@de.ibm.com>
 
        * regcache.c (registers_length): Remove.
index 2e049ee22a4cb783da42f52ffc0d046b2a40bc31..16d5442053fcad6cff49111525bd0d8b838a9167 100644 (file)
@@ -1080,6 +1080,24 @@ decode_xfer_write (char *buf, int packet_len, char **annex, CORE_ADDR *offset,
   return 0;
 }
 
+/* Decode the parameters of a qSearch:memory packet.  */
+
+int
+decode_search_memory_packet (const char *buf, int packet_len,
+                            CORE_ADDR *start_addrp,
+                            CORE_ADDR *search_space_lenp,
+                            gdb_byte *pattern, unsigned int *pattern_lenp)
+{
+  const char *p = buf;
+
+  p = decode_address_to_semicolon (start_addrp, p);
+  p = decode_address_to_semicolon (search_space_lenp, p);
+  packet_len -= p - buf;
+  *pattern_lenp = remote_unescape_input ((const gdb_byte *) p, packet_len,
+                                        pattern, packet_len);
+  return 0;
+}
+
 /* Ask GDB for the address of NAME, and return it in ADDRP if found.
    Returns 1 if the symbol is found, 0 if it is not, -1 on error.  */
 
index 852a0bd565f1ba3f6cf2d8de28c76930c28ac745..6993ae35e422c0f8d3b8177ee076a3713e715cef 100644 (file)
@@ -314,6 +314,153 @@ monitor_show_help (void)
   monitor_output ("    Quit GDBserver\n");
 }
 
+/* Subroutine of handle_search_memory to simplify it.  */
+
+static int
+handle_search_memory_1 (CORE_ADDR start_addr, CORE_ADDR search_space_len,
+                       gdb_byte *pattern, unsigned pattern_len,
+                       gdb_byte *search_buf,
+                       unsigned chunk_size, unsigned search_buf_size,
+                       CORE_ADDR *found_addrp)
+{
+  /* Prime the search buffer.  */
+
+  if (read_inferior_memory (start_addr, search_buf, search_buf_size) != 0)
+    {
+      warning ("unable to access target memory at 0x%lx, halting search",
+              (long) start_addr);
+      return -1;
+    }
+
+  /* Perform the search.
+
+     The loop is kept simple by allocating [N + pattern-length - 1] bytes.
+     When we've scanned N bytes we copy the trailing bytes to the start and
+     read in another N bytes.  */
+
+  while (search_space_len >= pattern_len)
+    {
+      gdb_byte *found_ptr;
+      unsigned nr_search_bytes = (search_space_len < search_buf_size
+                                 ? search_space_len
+                                 : search_buf_size);
+
+      found_ptr = memmem (search_buf, nr_search_bytes, pattern, pattern_len);
+
+      if (found_ptr != NULL)
+       {
+         CORE_ADDR found_addr = start_addr + (found_ptr - search_buf);
+         *found_addrp = found_addr;
+         return 1;
+       }
+
+      /* Not found in this chunk, skip to next chunk.  */
+
+      /* Don't let search_space_len wrap here, it's unsigned.  */
+      if (search_space_len >= chunk_size)
+       search_space_len -= chunk_size;
+      else
+       search_space_len = 0;
+
+      if (search_space_len >= pattern_len)
+       {
+         unsigned keep_len = search_buf_size - chunk_size;
+         CORE_ADDR read_addr = start_addr + keep_len;
+         int nr_to_read;
+
+         /* Copy the trailing part of the previous iteration to the front
+            of the buffer for the next iteration.  */
+         memcpy (search_buf, search_buf + chunk_size, keep_len);
+
+         nr_to_read = (search_space_len - keep_len < chunk_size
+                       ? search_space_len - keep_len
+                       : chunk_size);
+
+         if (read_inferior_memory (read_addr, search_buf + keep_len,
+                                   nr_to_read) != 0)
+           {
+             warning ("unable to access target memory at 0x%lx, halting search",
+                      (long) read_addr);
+             return -1;
+           }
+
+         start_addr += chunk_size;
+       }
+    }
+
+  /* Not found.  */
+
+  return 0;
+}
+
+/* Handle qSearch:memory packets.  */
+
+static void
+handle_search_memory (char *own_buf, int packet_len)
+{
+  CORE_ADDR start_addr;
+  CORE_ADDR search_space_len;
+  gdb_byte *pattern;
+  unsigned int pattern_len;
+  /* NOTE: also defined in find.c testcase.  */
+#define SEARCH_CHUNK_SIZE 16000
+  const unsigned chunk_size = SEARCH_CHUNK_SIZE;
+  /* Buffer to hold memory contents for searching.  */
+  gdb_byte *search_buf;
+  unsigned search_buf_size;
+  int found;
+  CORE_ADDR found_addr;
+  int cmd_name_len = sizeof ("qSearch:memory:") - 1;
+
+  pattern = malloc (packet_len);
+  if (pattern == NULL)
+    {
+      error ("unable to allocate memory to perform the search");
+      strcpy (own_buf, "E00");
+      return;
+    }
+  if (decode_search_memory_packet (own_buf + cmd_name_len,
+                                  packet_len - cmd_name_len,
+                                  &start_addr, &search_space_len,
+                                  pattern, &pattern_len) < 0)
+    {
+      free (pattern);
+      error ("error in parsing qSearch:memory packet");
+      strcpy (own_buf, "E00");
+      return;
+    }
+
+  search_buf_size = chunk_size + pattern_len - 1;
+
+  /* No point in trying to allocate a buffer larger than the search space.  */
+  if (search_space_len < search_buf_size)
+    search_buf_size = search_space_len;
+
+  search_buf = malloc (search_buf_size);
+  if (search_buf == NULL)
+    {
+      free (pattern);
+      error ("unable to allocate memory to perform the search");
+      strcpy (own_buf, "E00");
+      return;
+    }
+
+  found = handle_search_memory_1 (start_addr, search_space_len,
+                                 pattern, pattern_len,
+                                 search_buf, chunk_size, search_buf_size,
+                                 &found_addr);
+
+  if (found > 0)
+    sprintf (own_buf, "1,%lx", (long) found_addr);
+  else if (found == 0)
+    strcpy (own_buf, "0");
+  else
+    strcpy (own_buf, "E00");
+
+  free (search_buf);
+  free (pattern);
+}
+
 #define require_running(BUF)                   \
   if (!target_running ())                      \
     {                                          \
@@ -731,6 +878,13 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
       return;
     }
 
+  if (strncmp ("qSearch:memory:", own_buf, sizeof ("qSearch:memory:") - 1) == 0)
+    {
+      require_running (own_buf);
+      handle_search_memory (own_buf, packet_len);
+      return;
+    }
+
   /* Otherwise we didn't know what packet it was.  Say we didn't
      understand it.  */
   own_buf[0] = 0;
index 9d6c7512d75873f39d2c92e69c49c63a1e353048..d0520d237b1d05fe5c2b003304beee4e88c62424 100644 (file)
@@ -195,6 +195,10 @@ int decode_X_packet (char *from, int packet_len, CORE_ADDR * mem_addr_ptr,
 int decode_xfer_write (char *buf, int packet_len, char **annex,
                       CORE_ADDR *offset, unsigned int *len,
                       unsigned char *data);
+int decode_search_memory_packet (const char *buf, int packet_len,
+                                CORE_ADDR *start_addrp,
+                                CORE_ADDR *search_space_lenp,
+                                gdb_byte *pattern, unsigned int *pattern_lenp);
 
 int unhexify (char *bin, const char *hex, int count);
 int hexify (char *hex, const char *bin, int count);
index 831b23ce0338fe6c4e2c51bf8a470636ef131db7..8a28eb9cb5e4fddd79d2124686466ce46c06e30b 100644 (file)
@@ -935,6 +935,7 @@ enum {
   PACKET_qGetTLSAddr,
   PACKET_qSupported,
   PACKET_QPassSignals,
+  PACKET_qSearch_memory,
   PACKET_vAttach,
   PACKET_vRun,
   PACKET_MAX
@@ -6195,6 +6196,93 @@ remote_xfer_partial (struct target_ops *ops, enum target_object object,
   return strlen ((char *) readbuf);
 }
 
+static int
+remote_search_memory (struct target_ops* ops,
+                     CORE_ADDR start_addr, ULONGEST search_space_len,
+                     const gdb_byte *pattern, ULONGEST pattern_len,
+                     CORE_ADDR *found_addrp)
+{
+  struct remote_state *rs = get_remote_state ();
+  int max_size = get_memory_write_packet_size ();
+  struct packet_config *packet =
+    &remote_protocol_packets[PACKET_qSearch_memory];
+  /* number of packet bytes used to encode the pattern,
+     this could be more than PATTERN_LEN due to escape characters */
+  int escaped_pattern_len;
+  /* amount of pattern that was encodable in the packet */
+  int used_pattern_len;
+  int i;
+  int found;
+  ULONGEST found_addr;
+
+  /* Don't go to the target if we don't have to.
+     This is done before checking packet->support to avoid the possibility that
+     a success for this edge case means the facility works in general.  */
+  if (pattern_len > search_space_len)
+    return 0;
+  if (pattern_len == 0)
+    {
+      *found_addrp = start_addr;
+      return 1;
+    }
+
+  /* If we already know the packet isn't supported, fall back to the simple
+     way of searching memory.  */
+
+  if (packet->support == PACKET_DISABLE)
+    {
+      /* Target doesn't provided special support, fall back and use the
+        standard support (copy memory and do the search here).  */
+      return simple_search_memory (ops, start_addr, search_space_len,
+                                  pattern, pattern_len, found_addrp);
+    }
+
+  /* Insert header.  */
+  i = snprintf (rs->buf, max_size, 
+               "qSearch:memory:%s;%s;",
+               paddr_nz (start_addr),
+               phex_nz (search_space_len, sizeof (search_space_len)));
+  max_size -= (i + 1);
+
+  /* Escape as much data as fits into rs->buf.  */
+  escaped_pattern_len =
+    remote_escape_output (pattern, pattern_len, (rs->buf + i),
+                         &used_pattern_len, max_size);
+
+  /* Bail if the pattern is too large.  */
+  if (used_pattern_len != pattern_len)
+    error ("pattern is too large to transmit to remote target");
+
+  if (putpkt_binary (rs->buf, i + escaped_pattern_len) < 0
+      || getpkt_sane (&rs->buf, &rs->buf_size, 0) < 0
+      || packet_ok (rs->buf, packet) != PACKET_OK)
+    {
+      /* The request may not have worked because the command is not
+        supported.  If so, fall back to the simple way.  */
+      if (packet->support == PACKET_DISABLE)
+       {
+         return simple_search_memory (ops, start_addr, search_space_len,
+                                      pattern, pattern_len, found_addrp);
+       }
+      return -1;
+    }
+
+  if (rs->buf[0] == '0')
+    found = 0;
+  else if (rs->buf[0] == '1')
+    {
+      found = 1;
+      if (rs->buf[1] != ',')
+       error (_("unknown qSearch:memory reply: %s"), rs->buf);
+      unpack_varlen_hex (rs->buf + 2, &found_addr);
+      *found_addrp = found_addr;
+    }
+  else
+    error (_("unknown qSearch:memory reply: %s"), rs->buf);
+
+  return found;
+}
+
 static void
 remote_rcmd (char *command,
             struct ui_file *outbuf)
@@ -7256,6 +7344,7 @@ Specify the serial device it is connected to\n\
   remote_ops.to_flash_erase = remote_flash_erase;
   remote_ops.to_flash_done = remote_flash_done;
   remote_ops.to_read_description = remote_read_description;
+  remote_ops.to_search_memory = remote_search_memory;
   remote_ops.to_can_async_p = remote_return_zero;
   remote_ops.to_is_async_p = remote_return_zero;
 }
@@ -7403,6 +7492,7 @@ Specify the serial device it is connected to (e.g. /dev/ttya).";
   remote_async_ops.to_flash_erase = remote_flash_erase;
   remote_async_ops.to_flash_done = remote_flash_done;
   remote_async_ops.to_read_description = remote_read_description;
+  remote_async_ops.to_search_memory = remote_search_memory;
 }
 
 /* Set up the async extended remote vector by making a copy of the standard
@@ -7669,6 +7759,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
   add_packet_config_cmd (&remote_protocol_packets[PACKET_qSupported],
                         "qSupported", "supported-packets", 0);
 
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_qSearch_memory],
+                        "qSearch:memory", "search-memory", 0);
+
   add_packet_config_cmd (&remote_protocol_packets[PACKET_vFile_open],
                         "vFile:open", "hostio-open", 0);
 
index a8f1afb92748e92d6f1eef626d4d51ed669eb872..a9f92543e5b8320c5a0a8bca0bba6289db85faec 100644 (file)
@@ -476,6 +476,7 @@ update_current_target (void)
       INHERIT (to_make_corefile_notes, t);
       INHERIT (to_get_thread_local_address, t);
       /* Do not inherit to_read_description.  */
+      /* Do not inherit to_search_memory.  */
       INHERIT (to_magic, t);
       /* Do not inherit to_memory_map.  */
       /* Do not inherit to_flash_erase.  */
@@ -1760,6 +1761,157 @@ target_read_description (struct target_ops *target)
   return NULL;
 }
 
+/* The default implementation of to_search_memory.
+   This implements a basic search of memory, reading target memory and
+   performing the search here (as opposed to performing the search in on the
+   target side with, for example, gdbserver).  */
+
+int
+simple_search_memory (struct target_ops *ops,
+                     CORE_ADDR start_addr, ULONGEST search_space_len,
+                     const gdb_byte *pattern, ULONGEST pattern_len,
+                     CORE_ADDR *found_addrp)
+{
+  /* NOTE: also defined in find.c testcase.  */
+#define SEARCH_CHUNK_SIZE 16000
+  const unsigned chunk_size = SEARCH_CHUNK_SIZE;
+  /* Buffer to hold memory contents for searching.  */
+  gdb_byte *search_buf;
+  unsigned search_buf_size;
+  struct cleanup *old_cleanups;
+
+  search_buf_size = chunk_size + pattern_len - 1;
+
+  /* No point in trying to allocate a buffer larger than the search space.  */
+  if (search_space_len < search_buf_size)
+    search_buf_size = search_space_len;
+
+  search_buf = malloc (search_buf_size);
+  if (search_buf == NULL)
+    error (_("unable to allocate memory to perform the search"));
+  old_cleanups = make_cleanup (free_current_contents, &search_buf);
+
+  /* Prime the search buffer.  */
+
+  if (target_read (ops, TARGET_OBJECT_MEMORY, NULL,
+                  search_buf, start_addr, search_buf_size) != search_buf_size)
+    {
+      warning (_("unable to access target memory at %s, halting search"),
+              hex_string (start_addr));
+      do_cleanups (old_cleanups);
+      return -1;
+    }
+
+  /* Perform the search.
+
+     The loop is kept simple by allocating [N + pattern-length - 1] bytes.
+     When we've scanned N bytes we copy the trailing bytes to the start and
+     read in another N bytes.  */
+
+  while (search_space_len >= pattern_len)
+    {
+      gdb_byte *found_ptr;
+      unsigned nr_search_bytes = min (search_space_len, search_buf_size);
+
+      found_ptr = memmem (search_buf, nr_search_bytes,
+                         pattern, pattern_len);
+
+      if (found_ptr != NULL)
+       {
+         CORE_ADDR found_addr = start_addr + (found_ptr - search_buf);
+         *found_addrp = found_addr;
+         do_cleanups (old_cleanups);
+         return 1;
+       }
+
+      /* Not found in this chunk, skip to next chunk.  */
+
+      /* Don't let search_space_len wrap here, it's unsigned.  */
+      if (search_space_len >= chunk_size)
+       search_space_len -= chunk_size;
+      else
+       search_space_len = 0;
+
+      if (search_space_len >= pattern_len)
+       {
+         unsigned keep_len = search_buf_size - chunk_size;
+         CORE_ADDR read_addr = start_addr + keep_len;
+         int nr_to_read;
+
+         /* Copy the trailing part of the previous iteration to the front
+            of the buffer for the next iteration.  */
+         gdb_assert (keep_len == pattern_len - 1);
+         memcpy (search_buf, search_buf + chunk_size, keep_len);
+
+         nr_to_read = min (search_space_len - keep_len, chunk_size);
+
+         if (target_read (ops, TARGET_OBJECT_MEMORY, NULL,
+                          search_buf + keep_len, read_addr,
+                          nr_to_read) != nr_to_read)
+           {
+             warning (_("unable to access target memory at %s, halting search"),
+                      hex_string (read_addr));
+             do_cleanups (old_cleanups);
+             return -1;
+           }
+
+         start_addr += chunk_size;
+       }
+    }
+
+  /* Not found.  */
+
+  do_cleanups (old_cleanups);
+  return 0;
+}
+
+/* Search SEARCH_SPACE_LEN bytes beginning at START_ADDR for the
+   sequence of bytes in PATTERN with length PATTERN_LEN.
+
+   The result is 1 if found, 0 if not found, and -1 if there was an error
+   requiring halting of the search (e.g. memory read error).
+   If the pattern is found the address is recorded in FOUND_ADDRP.  */
+
+int
+target_search_memory (CORE_ADDR start_addr, ULONGEST search_space_len,
+                     const gdb_byte *pattern, ULONGEST pattern_len,
+                     CORE_ADDR *found_addrp)
+{
+  struct target_ops *t;
+  int found;
+
+  /* We don't use INHERIT to set current_target.to_search_memory,
+     so we have to scan the target stack and handle targetdebug
+     ourselves.  */
+
+  if (targetdebug)
+    fprintf_unfiltered (gdb_stdlog, "target_search_memory (%s, ...)\n",
+                       hex_string (start_addr));
+
+  for (t = current_target.beneath; t != NULL; t = t->beneath)
+    if (t->to_search_memory != NULL)
+      break;
+
+  if (t != NULL)
+    {
+      found = t->to_search_memory (t, start_addr, search_space_len,
+                                  pattern, pattern_len, found_addrp);
+    }
+  else
+    {
+      /* If a special version of to_search_memory isn't available, use the
+        simple version.  */
+      found = simple_search_memory (&current_target,
+                                   start_addr, search_space_len,
+                                   pattern, pattern_len, found_addrp);
+    }
+
+  if (targetdebug)
+    fprintf_unfiltered (gdb_stdlog, "  = %d\n", found);
+
+  return found;
+}
+
 /* Look through the currently pushed targets.  If none of them will
    be able to restart the currently running process, issue an error
    message.  */
index 8822a4061b1eac2193de256296d17d6d6ff2ed84..f1e414750edc67ff178e2f21b500ea812d258961 100644 (file)
@@ -505,6 +505,17 @@ struct target_ops
     int (*to_auxv_parse) (struct target_ops *ops, gdb_byte **readptr,
                          gdb_byte *endptr, CORE_ADDR *typep, CORE_ADDR *valp);
 
+    /* Search SEARCH_SPACE_LEN bytes beginning at START_ADDR for the
+       sequence of bytes in PATTERN with length PATTERN_LEN.
+
+       The result is 1 if found, 0 if not found, and -1 if there was an error
+       requiring halting of the search (e.g. memory read error).
+       If the pattern is found the address is recorded in FOUND_ADDRP.  */
+    int (*to_search_memory) (struct target_ops *ops,
+                            CORE_ADDR start_addr, ULONGEST search_space_len,
+                            const gdb_byte *pattern, ULONGEST pattern_len,
+                            CORE_ADDR *found_addrp);
+
     int to_magic;
     /* Need sub-structure for target machine related rather than comm related?
      */
@@ -1102,6 +1113,21 @@ extern int target_stopped_data_address_p (struct target_ops *);
 
 extern const struct target_desc *target_read_description (struct target_ops *);
 
+/* Utility implementation of searching memory.  */
+extern int simple_search_memory (struct target_ops* ops,
+                                 CORE_ADDR start_addr,
+                                 ULONGEST search_space_len,
+                                 const gdb_byte *pattern,
+                                 ULONGEST pattern_len,
+                                 CORE_ADDR *found_addrp);
+
+/* Main entry point for searching memory.  */
+extern int target_search_memory (CORE_ADDR start_addr,
+                                 ULONGEST search_space_len,
+                                 const gdb_byte *pattern,
+                                 ULONGEST pattern_len,
+                                 CORE_ADDR *found_addrp);
+
 /* Command logging facility.  */
 
 #define target_log_command(p)                                          \
index ee51ef7e38acbe279480ad1a7ebdb0b5fda6e138..25618eee7645bf8ba37db7f2b374ae48f328e40f 100644 (file)
@@ -1,3 +1,8 @@
+2008-05-09  Doug Evans  <dje@google.com>
+
+       * testsuite/gdb.base/find.exp: New file.
+       * testsuite/gdb.base/find.c: New file.
+
 2008-05-08  Daniel Jacobowitz  <dan@codesourcery.com>
 
        * gdb.base/commands.exp (watchpoint_command_test): Handle
diff --git a/gdb/testsuite/gdb.base/find.c b/gdb/testsuite/gdb.base/find.c
new file mode 100644 (file)
index 0000000..bcd3451
--- /dev/null
@@ -0,0 +1,62 @@
+/* Testcase for the find command.
+   This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2008 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/>.
+
+   Please email any bugs, comments, and/or additions to this file to:
+   bug-gdb@gnu.org  */
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#define CHUNK_SIZE 16000 /* same as findcmd.c's */
+#define BUF_SIZE (2 * CHUNK_SIZE) /* at least two chunks */
+
+static int8_t int8_search_buf[100];
+static int16_t int16_search_buf[100];
+static int32_t int32_search_buf[100];
+static int64_t int64_search_buf[100];
+
+static char *search_buf;
+static int search_buf_size;
+
+static int x;
+
+static void
+stop_here ()
+{
+  x = 1; // stop here
+}
+
+static void
+init_bufs ()
+{
+  search_buf_size = BUF_SIZE;
+  search_buf = malloc (search_buf_size);
+  if (search_buf == NULL)
+    exit (1);
+  memset (search_buf, 'x', search_buf_size);
+}
+
+int
+main ()
+{
+  init_bufs ();
+
+  stop_here ();
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.base/find.exp b/gdb/testsuite/gdb.base/find.exp
new file mode 100644 (file)
index 0000000..11e05b5
--- /dev/null
@@ -0,0 +1,191 @@
+# Copyright 2008 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/>.
+
+# Please email any bugs, comments, and/or additions to this file to:
+# bug-gdb@prep.ai.mit.edu
+
+# This tests the find command.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+set testfile "find"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug nowarnings}] != "" } {
+    untested find.exp
+    return -1
+}
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+gdb_test "break $srcfile:stop_here" \
+    "Breakpoint.*at.* file .*$srcfile, line.*" \
+    "breakpoint function in file"
+
+gdb_run_cmd
+gdb_expect {
+    -re "Breakpoint \[0-9\]+,.*stop_here.* at .*$srcfile:.*$gdb_prompt $" {
+       pass "run until function breakpoint"
+    }
+    -re "$gdb_prompt $" {
+       fail "run until function breakpoint"
+    }
+    timeout {
+       fail "run until function breakpoint (timeout)"
+    }
+}
+
+# We've now got the target program in a state where we can test "find".
+
+set hex_number {0x[0-9a-fA-F][0-9a-fA-F]*}
+set history_prefix {[$][0-9]* = }
+set newline {[\r\n]*}
+set pattern_not_found "${newline}pattern not found"
+set one_pattern_found "${newline}1 pattern found"
+set two_patterns_found "${newline}2 patterns found"
+
+# Test string pattern.
+
+gdb_test "set *(int32_t*) &int8_search_buf\[10\] = 0x61616161" "" ""
+
+gdb_test "find &int8_search_buf\[0\], +sizeof(int8_search_buf), 'a', 'a', 'a'" \
+    "${hex_number}.*<int8_search_buf\\+10>${newline}${hex_number}.*<int8_search_buf\\+11>${two_patterns_found}" \
+    "find string pattern"
+
+# Test not finding pattern because search range too small, with
+# potential find at the edge of the range.
+
+gdb_test "find &int8_search_buf\[0\], +10+3, \"aaaa\"" \
+    "${pattern_not_found}" \
+    "pattern not found at end of range"
+
+# Increase the search range by 1 and we should find the pattern.
+
+gdb_test "find &int8_search_buf\[0\], +10+3+1, 'a', 'a', 'a', 'a'" \
+    "${hex_number}.*<int8_search_buf\\+10>${one_pattern_found}" \
+    "pattern found at end of range"
+
+# Test max-count, $_ and $numfound.
+
+gdb_test "find /1 &int8_search_buf\[0\], +sizeof(int8_search_buf), 'a', 'a', 'a'" \
+    "${hex_number}.*<int8_search_buf\\+10>${one_pattern_found}" \
+    "max-count"
+
+gdb_test "print \$_" \
+    "${history_prefix}.*${hex_number}" \
+    "\$_"
+
+gdb_test "print \$numfound" \
+    "${history_prefix}1" \
+    "\$numfound"
+
+# Test max-count with size-char.
+# They can be specified in either order.
+
+gdb_test "find /1b &int8_search_buf\[0\], +sizeof(int8_search_buf), 0x61, 0x61, 0x61" \
+    "${hex_number}.*<int8_search_buf\\+10>${one_pattern_found}" \
+    "size,max-count, /1b"
+
+gdb_test "find /b1 &int8_search_buf\[0\], +sizeof(int8_search_buf), 0x61, 0x61, 0x61" \
+    "${hex_number}.*<int8_search_buf\\+10>${one_pattern_found}" \
+    "size,max-count, /b1"
+
+gdb_test "find /b /1 &int8_search_buf\[0\], +sizeof(int8_search_buf), 0x61, 0x61, 0x61" \
+    "${hex_number}.*<int8_search_buf\\+10>${one_pattern_found}" \
+    "size,max-count, /b/1"
+
+gdb_test "find /1 /b &int8_search_buf\[0\], +sizeof(int8_search_buf), 0x61, 0x61, 0x61" \
+    "${hex_number}.*<int8_search_buf\\+10>${one_pattern_found}" \
+    "size,max-count, /1/b"
+
+# Test specifying end address.
+
+gdb_test "find /b &int8_search_buf\[0\], &int8_search_buf\[0\]+sizeof(int8_search_buf), 0x61, 0x61, 0x61, 0x61" \
+    "${hex_number}.*<int8_search_buf\\+10>${one_pattern_found}" \
+    "find byte pattern with end address"
+
+# Test 16-bit pattern.
+
+gdb_test "set int16_search_buf\[10\] = 0x1234" "" ""
+
+gdb_test "find /h &int16_search_buf\[0\], +sizeof(int16_search_buf), 0x1234" \
+    "${hex_number}.*<int16_search_buf\\+20>${one_pattern_found}" \
+    "find 16-bit pattern"
+
+gdb_test "find &int16_search_buf\[0\], +sizeof(int16_search_buf), (int16_t) 0x1234" \
+    "${hex_number}.*<int16_search_buf\\+20>${one_pattern_found}" \
+    "find 16-bit pattern"
+
+# Test 32-bit pattern.
+
+gdb_test "set int32_search_buf\[10\] = 0x12345678" "" ""
+
+gdb_test "find &int32_search_buf\[0\], +sizeof(int32_search_buf), (int32_t) 0x12345678" \
+    "${hex_number}.*<int32_search_buf\\+40>${one_pattern_found}" \
+    "find 32-bit pattern"
+
+gdb_test "find /w &int32_search_buf\[0\], +sizeof(int32_search_buf), 0x12345678" \
+    "${hex_number}.*<int32_search_buf\\+40>${one_pattern_found}" \
+    "find 32-bit pattern"
+
+# Test 64-bit pattern.
+
+gdb_test "set int64_search_buf\[10\] = 0xfedcba9876543210LL" "" ""
+
+gdb_test "find &int64_search_buf\[0\], +sizeof(int64_search_buf), (int64_t) 0xfedcba9876543210LL" \
+    "${hex_number}.*<int64_search_buf\\+80>${one_pattern_found}" \
+    "find 64-bit pattern"
+
+gdb_test "find /g &int64_search_buf\[0\], +sizeof(int64_search_buf), 0xfedcba9876543210LL" \
+    "${hex_number}.*<int64_search_buf\\+80>${one_pattern_found}" \
+    "find 64-bit pattern"
+
+# Test mixed-sized patterns.
+
+gdb_test "set *(int8_t*) &search_buf\[10\] = 0x62" "" ""
+gdb_test "set *(int16_t*) &search_buf\[11\] = 0x6363" "" ""
+gdb_test "set *(int32_t*) &search_buf\[13\] = 0x64646464" "" ""
+
+gdb_test "find &search_buf\[0\], +100, (int8_t) 0x62, (int16_t) 0x6363, (int32_t) 0x64646464" \
+    "${hex_number}${one_pattern_found}" \
+    "find mixed-sized pattern"
+
+# Test search spanning a large range, in the particular case of native
+# targets, test the search spanning multiple chunks.
+# Remote targets may implement the search differently.
+
+set CHUNK_SIZE 16000 ;# see findcmd.c
+
+gdb_test "set *(int32_t*) &search_buf\[0*${CHUNK_SIZE}+100\] = 0x12345678" "" ""
+gdb_test "set *(int32_t*) &search_buf\[1*${CHUNK_SIZE}+100\] = 0x12345678" "" ""
+
+gdb_test "find /w search_buf, +search_buf_size, 0x12345678" \
+    "${hex_number}${newline}${hex_number}${two_patterns_found}" \
+    "search spanning large range"
+
+# For native targets, test a pattern straddling a chunk boundary.
+
+if [isnative] {
+    gdb_test "set *(int32_t*) &search_buf\[${CHUNK_SIZE}-1\] = 0xfdb97531" "" ""
+    gdb_test "find /w search_buf, +search_buf_size, 0xfdb97531" \
+    "${hex_number}${one_pattern_found}" \
+    "find pattern straddling chunk boundary"
+}