public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [python][patch] Inferior and Thread information support.
@ 2010-05-24 13:36 Phil Muldoon
  2010-05-24 18:06 ` Eli Zaretskii
  2010-06-10 18:40 ` Tom Tromey
  0 siblings, 2 replies; 21+ messages in thread
From: Phil Muldoon @ 2010-05-24 13:36 UTC (permalink / raw)
  To: gdb-patches ml

This patch adds Python support to inferiors and threads.  The support
is purely informational (I.E., does not support GDB control of the
inferior).  This patch was checked in by Thiago to the Archer branch
last year, and largely remains as it was there.  I've adjusted it to
work with the current GDB view, bug fixes, and the usual work that
goes on in merging code.

Tested on x8664 with no regressions

OK?

Cheers

Phil

--
2010-05-24  Phil Muldoon  <pmuldoon@redhat.com>
            Tom Tromey  <tromey@redhat.com>
            Thiago Jung Bauermann  <bauerman@br.ibm.com>

	* value.c (pack_unsigned_long): New function.
	(value_from_ulongest): New function.
	* value.h (value_from_ulongest): Declare.
	* python/python.c (_initialize_python): Call
	gdbpy_initialize_thread and gdbpy_initialize_inferior.
	* python/python-internal.h: Define thread_object.
	(gdbpy_inferiors, gdbpy_selected_thread)
	(frame_info_to_frame_object, create_thread_object)
	(find_thread_object, find_inferior_object)
	(gdbpy_initialize_thread, gdbpy_initialize_inferiors)
	(gdbpy_is_value_object, get_addr_from_python): Declare.
	* python/py-value.c (builtin_type_upylong): Define.
	(convert_value_from_python): Add logic for ulongest.
	(gdbpy_is_value_object): New function.
	* python/py-utils.c (get_addr_from_python): New function.
	* python/py-frame.c (frame_info_to_frame_object): Return a PyObject.
	(gdbpy_selected_frame): Use PyObject over frame_info.
	* target.h (allocate_pattern_buffer, increase_pattern_buffer)
	(search_memory, put_bits): Declare.
	* findcmd.c (put_bits): Remove static declaration.
	(allocate_pattern_buffer): New function.
	(increase_pattern_buffer): New function.
	(search_memory): New function.
	(parse_find_args): Move INITIAL_PATTERN_BUF_SIZE definition to
	allocate_pattern_buffer.  Use allocate_pattern_buffer and
	increase_pattern_buffer.
	(find_command): Use search_memory.
	* Makefile.in (SUBDIR_PYTHON_OBS): Add py-inferior and
	py-infthread.
	(SUBDIR_PYTHON_SRCS): Likewise.
	(py-inferior.o): New Rule.
	(py-infthread.o): New Rule.
	* python/py-inferior.c: New File.
	* python/py-infthread.c: New File.

Testsuite ChangeLog

2010-05-24  Phil Muldoon  <pmuldoon@redhat.com>
            Tom Tromey  <tromey@redhat.com>
            Thiago Jung Bauermann  <bauerman@br.ibm.com>

	* gdb.python/py-inferior.c: New File.
	* gdb.python/py-infthread.c: New File.
	* gdb.python/py-inferior.exp: New File.
	* gdb.python/py-infthread.exp: New File.

Documentation ChangeLog

2010-05-24  Phil Muldoon  <pmuldoon@redhat.com>
	    Tom Tromey  <tromey@redhat.com>
	    Thiago Jung Bauermann  <bauerman@br.ibm.com>

	* gdb.texinfo (Inferiors In Python): New node.
	* gdb.texinfo (Threads In Python): New node.

--

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 754671f..1e9a622 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -273,6 +273,8 @@ SUBDIR_PYTHON_OBS = \
 	py-cmd.o \
 	py-frame.o \
 	py-function.o \
+	py-inferior.o \
+	py-infthread.o \
 	py-lazy-string.o \
 	py-objfile.o \
 	py-param.o \
@@ -291,6 +293,8 @@ SUBDIR_PYTHON_SRCS = \
 	python/py-cmd.c \
 	python/py-frame.c \
 	python/py-function.c \
+	python/py-inferior.c \
+	python/py-infthread.c \
 	python/py-lazy-string.c \
 	python/py-objfile.c \
 	python/py-param.c \
@@ -2017,6 +2021,14 @@ py-function.o: $(srcdir)/python/py-function.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-function.c
 	$(POSTCOMPILE)
 
+py-inferior.o: $(srcdir)/python/py-inferior.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-inferior.c
+	$(POSTCOMPILE)
+
+py-infthread.o: $(srcdir)/python/py-infthread.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-infthread.c
+	$(POSTCOMPILE)
+
 py-lazy-string.o: $(srcdir)/python/py-lazy-string.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-lazy-string.c
 	$(POSTCOMPILE)
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index e929481..f7a1370 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -19940,6 +19940,8 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
 * Types In Python::             Python representation of types.
 * Pretty Printing API::         Pretty-printing values.
 * Selecting Pretty-Printers::   How GDB chooses a pretty-printer.
+* Inferiors In Python::         Python representation of inferiors (processes)
+* Threads In Python::           Accessing inferior threads from Python.
 * Commands In Python::          Implementing new commands in Python.
 * Parameters In Python::        Adding new @value{GDBN} parameters.
 * Functions In Python::         Writing new convenience functions.
@@ -20711,6 +20713,143 @@ import gdb.libstdcxx.v6
 gdb.libstdcxx.v6.register_printers (gdb.current_objfile ())
 @end smallexample
 
+@node Inferiors In Python
+@subsubsection Inferiors In Python
+
+Programs which are being run under @value{GDBN} are called inferiors
+(@pxref{Inferiors and Programs}).  Python scripts can access
+information about and manipulate inferiors controlled by @value{GDBN}
+via objects of the @code{gdb.Inferior} class.
+
+The following inferior-related functions are available in the @code{gdb}
+module:
+
+@defun inferiors
+Return a tuple containing all inferior objects.
+@end defun
+
+A @code{gdb.Inferior} object has the following attributes:
+
+@table @code
+@defivar Inferior num
+ID of inferior, as assigned by GDB.
+@end defivar
+
+@defivar Inferior pid
+Process ID of the inferior, as assigned by the underlying operating
+system.
+@end defivar
+
+@defivar Inferior was_attached
+Boolean signaling whether the inferior was created using `attach', or
+started by @value{GDBN} itself.
+@end defivar
+@end table
+
+A @code{gdb.Inferior} object has the following methods:
+
+@table @code
+@defmethod Inferior threads
+This method returns a tuple holding all the threads which are valid
+when it is called.  If there are no valid threads, the method will
+return an empty list.
+@end defmethod
+
+@findex gdb.read_memory
+@defmethod Inferior read_memory @var{address} @var{length}
+Read @var{length} bytes of memory from the inferior, starting at
+@var{address}.  Returns a buffer object, which behaves much like an array
+or a string.  It can be modified and given to the @code{gdb.write_memory}
+function.
+@end defmethod
+
+@findex gdb.write_memory
+@defmethod Inferior write_memory @var{address} @var{buffer} @r{[}@var{length}@r{]}
+Write the contents of @var{buffer} (a Python object which supports the
+buffer protocol, i.e., a string, an array or the object returned from
+@code{gdb.read_memory}) to the inferior, starting at @var{address}.
+If given, @var{length} determines the number of bytes from @var{buffer} to
+be written.
+@end defmethod
+
+@findex gdb.search_memory
+@defmethod Inferior search_memory @var{address} @var{length} @var{pattern} @r{[}@var{size}@r{]} @r{[}@var{max_count}@r{]}
+Search a region of the inferior memory starting at @var{address} with the
+given @var{length}.  @var{pattern} can be a string, a byte array, a buffer
+object, a number, a @code{gdb.Value} object (@pxref{Values From Inferior})
+or a list or tuple with elements in any combination of those types.  If
+@var{size} is given and is non-zero, it specifies the size in bytes of a
+Python scalar or @code{gdb.Value} in the search pattern.  If @var{size} is
+zero or 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.  @var{max_count} is the
+highest number of matches to search for.
+@end defmethod
+@end table
+
+@node Threads In Python
+@subsubsection Threads In Python
+
+Python scripts can access information about, and manipulate inferior threads
+controlled by @value{GDBN}, via objects of the @code{gdb.InferiorThread} class.
+
+The following thread-related functions are available in the @code{gdb}
+module:
+
+@findex gdb.selected_thread
+@defun selected_thread
+This function returns the thread object for the selected thread.  If there
+is no selected thread, this will return @code{None}.
+@end defun
+
+A @code{gdb.InferiorThread} object has the following attributes:
+
+@table @code
+@defivar InferiorThread num
+ID of the thread, as assigned by GDB.
+@end defivar
+
+@defivar InferiorThread ptid
+ID of the thread, as assigned by the operating system.  This attribute is a
+tuple containing three integers.  The first is the Process ID (PID); the second
+is the Lightweight Process ID (TID), and the third is the Thread ID (TID).
+Either the TID or TID may be 0, which indicates that the operating system
+does not  use that identifier.
+@end defivar
+@end table
+
+A @code{gdb.InferiorThread} object has the following methods:
+
+@table @code
+@defmethod InferiorThread frames
+Return a tuple containing all frames in the thread.
+@end defmethod
+
+@defmethod InferiorThread newest_frame
+Return the newest frame thread's stack.
+@end defmethod
+
+@defmethod InferiorThread switch
+This changes @value{GDBN}'s currently selected thread to the one represented
+by this object.
+@end defmethod
+
+@defmethod InferiorThread is_stopped
+Return a Boolean indicating whether the thread is stopped.
+@end defmethod
+
+@defmethod InferiorThread is_running
+Return a Boolean indicating whether the thread is running.
+@end defmethod
+
+@defmethod InferiorThread is_exited
+Return a Boolean indicating whether the thread is exited.
+@end defmethod
+@end table
+
+
 @node Commands In Python
 @subsubsection Commands In Python
 
diff --git a/gdb/findcmd.c b/gdb/findcmd.c
index 8e9a4b8..1e8decc 100644
--- a/gdb/findcmd.c
+++ b/gdb/findcmd.c
@@ -27,7 +27,7 @@
 
 /* Copied from bfd_put_bits.  */
 
-static void
+void
 put_bits (bfd_uint64_t data, char *buf, int bits, bfd_boolean big_p)
 {
   int i;
@@ -45,6 +45,41 @@ put_bits (bfd_uint64_t data, char *buf, int bits, bfd_boolean big_p)
     }
 }
 
+/* Allocates a buffer in *PATTERN_BUF, with a hard-coded initial size which
+   will be returned in *PATTERN_BUF_SIZE.  *PATTERN_BUF_END points to the same
+   place as *PATTERN_BUF, indicating that the buffer is initially empty.  */
+
+void
+allocate_pattern_buffer (char **pattern_buf, char **pattern_buf_end,
+			 ULONGEST *pattern_buf_size)
+{
+#define INITIAL_PATTERN_BUF_SIZE 100
+  *pattern_buf_size = INITIAL_PATTERN_BUF_SIZE;
+  *pattern_buf = xmalloc (*pattern_buf_size);
+  *pattern_buf_end = *pattern_buf;
+}
+
+/* Grows *PATTERN_BUF by a factor of two if it's not large enough to hold
+   VAL_BYTES more bytes  or a 64-bit value, whichever is larger.
+   *PATTERN_BUF_END is updated as necessary.  */
+
+void
+increase_pattern_buffer (char **pattern_buf, char **pattern_buf_end,
+			 ULONGEST *pattern_buf_size, int val_bytes)
+{
+    /* 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;
+      }
+}
+
 /* Subroutine of find_command to simplify it.
    Parse the arguments of the "find" command.  */
 
@@ -61,8 +96,7 @@ parse_find_args (char *args, ULONGEST *max_countp,
   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;
+  ULONGEST pattern_buf_size;
   /* Pointer to one past the last in-use part of pattern_buf.  */
   char *pattern_buf_end;
   ULONGEST pattern_len;
@@ -75,8 +109,7 @@ parse_find_args (char *args, ULONGEST *max_countp,
   if (args == NULL)
     error (_("Missing search parameters."));
 
-  pattern_buf = xmalloc (pattern_buf_size);
-  pattern_buf_end = pattern_buf;
+  allocate_pattern_buffer (&pattern_buf, &pattern_buf_end, &pattern_buf_size);
   old_cleanups = make_cleanup (free_current_contents, &pattern_buf);
 
   /* Get search granularity and/or max count if specified.
@@ -175,18 +208,8 @@ parse_find_args (char *args, ULONGEST *max_countp,
       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;
-	}
-
+      increase_pattern_buffer (&pattern_buf, &pattern_buf_end,
+			       &pattern_buf_size, val_bytes);
       if (size != '\0')
 	{
 	  x = value_as_long (v);
@@ -240,6 +263,45 @@ parse_find_args (char *args, ULONGEST *max_countp,
   discard_cleanups (old_cleanups);
 }
 
+/* Drives target_search_memory to sweep through the specified search space,
+   possibly in several iterations (with one call to this function for each
+   iteration).  *START_ADDR is the address where the search starts, and is
+   updated to the next starting address to continue the search.
+   *SEARCH_SPACE_LEN is the amount of bytes which will be searched, and is
+   updated for the next iteration.  PATTERN_BUF holds the pattern to
+   be searched  for, PATTERN_LEN is the size of the pattern in bytes.
+   If a match is found, it's address is put in *FOUND_ADDR.
+
+   Returns 1 if found, 0 if not found, and -1 if there was an error
+   requiring halting of the search (e.g. memory read error).  */
+
+int
+search_memory (CORE_ADDR *start_addr, ULONGEST *search_space_len,
+	       const char *pattern_buf, ULONGEST pattern_len,
+	       CORE_ADDR *found_addr)
+{
+  /* Offset from start of this iteration to the next iteration.  */
+  ULONGEST next_iter_incr;
+  int found;
+
+  found = target_search_memory (*start_addr, *search_space_len,
+				pattern_buf, pattern_len, found_addr);
+  if (found <= 0)
+    return found;
+
+  /* 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;
+
+  return found;
+}
+
 static void
 find_command (char *args, int from_tty)
 {
@@ -270,12 +332,11 @@ find_command (char *args, int from_tty)
   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);
+      int found;
 
+      found = search_memory (&start_addr, &search_space_len, pattern_buf,
+			     pattern_len, &found_addr);
       if (found <= 0)
 	break;
 
@@ -283,16 +344,6 @@ find_command (char *args, int from_tty)
       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.  */
diff --git a/gdb/python/py-frame.c b/gdb/python/py-frame.c
index 6e9e5d3..a7df810 100644
--- a/gdb/python/py-frame.c
+++ b/gdb/python/py-frame.c
@@ -265,7 +265,7 @@ frapy_function (PyObject *self, PyObject *args)
 /* Convert a frame_info struct to a Python Frame object.
    Sets a Python exception and returns NULL on error.  */
 
-static frame_object *
+PyObject *
 frame_info_to_frame_object (struct frame_info *frame)
 {
   frame_object *frame_obj;
@@ -296,7 +296,7 @@ frame_info_to_frame_object (struct frame_info *frame)
 
   frame_obj->gdbarch = get_frame_arch (frame);
 
-  return frame_obj;
+  return (PyObject *) frame_obj;
 }
 
 /* Implementation of gdb.Frame.older (self) -> gdb.Frame.
@@ -497,7 +497,7 @@ PyObject *
 gdbpy_selected_frame (PyObject *self, PyObject *args)
 {
   struct frame_info *frame;
-  frame_object *frame_obj = NULL;   /* Initialize to appease gcc warning.  */
+  PyObject *frame_obj = NULL;   /* Initialize to appease gcc warning.  */
   volatile struct gdb_exception except;
 
   TRY_CATCH (except, RETURN_MASK_ALL)
@@ -507,7 +507,7 @@ gdbpy_selected_frame (PyObject *self, PyObject *args)
     }
   GDB_PY_HANDLE_EXCEPTION (except);
 
-  return (PyObject *) frame_obj;
+  return frame_obj;
 }
 
 /* Implementation of gdb.stop_reason_string (Integer) -> String.
diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c
new file mode 100644
index 0000000..142f54b
--- /dev/null
+++ b/gdb/python/py-inferior.c
@@ -0,0 +1,934 @@
+/* Python interface to inferiors.
+
+   Copyright (C) 2009, 2010 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 "exceptions.h"
+#include "gdbcore.h"
+#include "gdbthread.h"
+#include "inferior.h"
+#include "observer.h"
+#include "python-internal.h"
+#include "arch-utils.h"
+#include "language.h"
+
+struct threadlist_entry {
+  thread_object *thread_obj;
+  struct threadlist_entry *next;
+};
+
+typedef struct
+{
+  PyObject_HEAD
+
+  /* The inferior we represent.  */
+  struct inferior *inferior;
+
+  /* thread_object instances under this inferior.  This list owns a
+     reference to each object it contains.  */
+  struct threadlist_entry *threads;
+
+  /* Number of threads in the list.  */
+  int nthreads;
+} inferior_object;
+
+static PyTypeObject inferior_object_type;
+
+typedef struct {
+  PyObject_HEAD
+  void *buffer;
+
+  /* These are kept just for mbpy_str.  */
+  CORE_ADDR addr;
+  CORE_ADDR length;
+} membuf_object;
+
+static PyTypeObject membuf_object_type;
+
+/* Require that INFERIOR be a valid inferior ID.  */
+#define INFPY_REQUIRE_VALID(Inferior)				\
+  do {								\
+    if (!Inferior->inferior)					\
+      {								\
+	PyErr_SetString (PyExc_RuntimeError,			\
+			 _("Inferior no longer exists."));	\
+	return NULL;						\
+      }								\
+  } while (0)
+
+struct inflist_entry {
+  inferior_object *inf_obj;
+  struct inflist_entry *next;
+};
+
+\f
+
+/* Inferior objects list.  */
+
+/* List containing inferior_objects.  This list owns a reference to each
+   object it contains.  */
+static struct inflist_entry *gdbpy_inferior_list;
+
+static int ninferiors;
+
+
+/* An observer callback function that is called when an inferior has
+   been created.  Creates a corresponding Python object for the inferior
+   and adds it to the list.  */
+static void
+add_inferior_object (struct inferior *inf)
+{
+  inferior_object *inf_obj;
+  struct inflist_entry *entry;
+  struct cleanup *cleanup;
+
+  if (!inf)
+    {
+      warning (_("Cannot create Python Inferior object."));
+      return;
+    }
+
+  /* While creating new inferior no inferior thread is available.
+     Therefore get_current_arch has no valid current frame (and it
+     would crash).  */
+  cleanup = ensure_python_env (target_gdbarch, current_language);
+
+  inf_obj = PyObject_New (inferior_object, &inferior_object_type);
+  if (!inf_obj)
+    {
+      warning (_("Cannot create Python Inferior object."));
+      gdbpy_print_stack ();
+      do_cleanups (cleanup);
+      return;
+    }
+
+  inf_obj->inferior = inf;
+  inf_obj->threads = NULL;
+  inf_obj->nthreads = 0;
+
+  entry = xmalloc (sizeof (struct inflist_entry));
+  entry->inf_obj = inf_obj;
+  entry->next = gdbpy_inferior_list;
+
+  gdbpy_inferior_list = entry;
+
+  ninferiors++;
+
+  do_cleanups (cleanup);
+}
+
+/* An observer callback function that is called when an inferior has
+   been deleted.  Removes the corresponding Python object from the
+   inferior list, and removes the list's reference to the object.  */
+static void
+delete_inferior_object (struct inferior *inf)
+{
+  struct cleanup *cleanup;
+  struct inflist_entry **inf_entry, *inf_tmp;
+  struct threadlist_entry *th_entry, *th_tmp;
+
+  /* Find inferior_object for the given PID.  */
+  for (inf_entry = &gdbpy_inferior_list; *inf_entry != NULL;
+       inf_entry = &(*inf_entry)->next)
+    if ((*inf_entry)->inf_obj->inferior->pid == inf->pid)
+      break;
+
+  if (!*inf_entry)
+    return;
+
+  cleanup = ensure_python_env (get_current_arch (), current_language);
+
+  inf_tmp = *inf_entry;
+  inf_tmp->inf_obj->inferior = NULL;
+
+  /* Deallocate threads list.  */
+  for (th_entry = inf_tmp->inf_obj->threads; th_entry != NULL;)
+    {
+      Py_DECREF (th_entry->thread_obj);
+
+      th_tmp = th_entry;
+      th_entry = th_entry->next;
+      xfree (th_tmp);
+    }
+
+  inf_tmp->inf_obj->nthreads = 0;
+
+  *inf_entry = (*inf_entry)->next;
+  Py_DECREF (inf_tmp->inf_obj);
+  xfree (inf_tmp);
+
+  ninferiors--;
+
+  do_cleanups (cleanup);
+}
+
+/* Finds the Python Inferior object for the given pid.  Returns a borrowed
+   reference.  */
+PyObject *
+find_inferior_object (int pid)
+{
+  struct inflist_entry *p;
+
+  for (p = gdbpy_inferior_list; p != NULL; p = p->next)
+    if (p->inf_obj->inferior->pid == pid)
+      return (PyObject *) p->inf_obj;
+
+  return NULL;
+}
+
+/* Finds the Python InferiorThread object for the given ptid.  Returns a
+   borrowed reference.  */
+thread_object *
+find_thread_object (ptid_t ptid)
+{
+  int pid;
+  struct inflist_entry *p;
+  struct threadlist_entry *q;
+
+  pid = PIDGET (ptid);
+  for (p = gdbpy_inferior_list; p != NULL; p = p->next)
+    if (p->inf_obj->inferior->pid == pid)
+      for (q = p->inf_obj->threads; q != NULL; q = q->next)
+	if (ptid_equal (q->thread_obj->thread->ptid, ptid))
+	  return q->thread_obj;
+
+  return NULL;
+}
+
+\f
+
+/* Inferior object.  */
+
+static void
+add_thread_object (struct thread_info *tp)
+{
+  struct cleanup *cleanup;
+  thread_object *thread_obj;
+  inferior_object *inf_obj;
+  struct threadlist_entry *entry;
+
+  /* Note that the arch does not matter here, because we cannot
+     execute arbitrary Python code.  Calling get_current_arch here
+     will crash.  */
+  cleanup = ensure_python_env (target_gdbarch, current_language);
+
+  thread_obj = create_thread_object (tp);
+  if (!thread_obj)
+    {
+      warning (_("Cannot create Python InferiorThread object."));
+      gdbpy_print_stack ();
+      do_cleanups (cleanup);
+      return;
+    }
+
+  inf_obj = (inferior_object *) thread_obj->inf_obj;
+
+  entry = xmalloc (sizeof (struct threadlist_entry));
+  entry->thread_obj = thread_obj;
+  entry->next = inf_obj->threads;
+
+  inf_obj->threads = entry;
+  inf_obj->nthreads++;
+
+  do_cleanups (cleanup);
+}
+
+static void
+delete_thread_object (struct thread_info *tp, int ignore)
+{
+  struct cleanup *cleanup;
+  inferior_object *inf_obj;
+  thread_object *thread_obj;
+  struct threadlist_entry **entry, *tmp;
+
+  inf_obj = (inferior_object *) find_inferior_object (PIDGET(tp->ptid));
+  if (!inf_obj)
+    return;
+
+  /* Find thread entry in its inferior's thread_list.  */
+  for (entry = &inf_obj->threads; *entry != NULL; entry =
+	 &(*entry)->next)
+    if ((*entry)->thread_obj->thread == tp)
+      break;
+
+  if (!*entry)
+    return;
+
+  cleanup = ensure_python_env (get_current_arch (), current_language);
+
+  tmp = *entry;
+  tmp->thread_obj->thread = NULL;
+
+  *entry = (*entry)->next;
+  inf_obj->nthreads--;
+
+  Py_DECREF (tmp->thread_obj);
+  xfree (tmp);
+
+  do_cleanups (cleanup);
+}
+
+static PyObject *
+infpy_threads (PyObject *self, PyObject *args)
+{
+  int i;
+  struct threadlist_entry *entry;
+  inferior_object *inf_obj = (inferior_object *) self;
+  PyObject *tuple;
+
+  INFPY_REQUIRE_VALID (inf_obj);
+
+  tuple = PyTuple_New (inf_obj->nthreads);
+  if (!tuple)
+    return NULL;
+
+  /* The list is in reverse order of thread age (i.e., newest comes
+     first).  */
+  for (i = 0, entry = inf_obj->threads; i < inf_obj->nthreads;
+       i++, entry = entry->next)
+    {
+      Py_INCREF (entry->thread_obj);
+      PyTuple_SET_ITEM (tuple, i, (PyObject *) entry->thread_obj);
+    }
+
+  return tuple;
+}
+
+static PyObject *
+infpy_get_num (PyObject *self, void *closure)
+{
+  inferior_object *inf = (inferior_object *) self;
+
+  INFPY_REQUIRE_VALID (inf);
+
+  return PyLong_FromLong (inf->inferior->num);
+}
+
+static PyObject *
+infpy_get_pid (PyObject *self, void *closure)
+{
+  inferior_object *inf = (inferior_object *) self;
+
+  INFPY_REQUIRE_VALID (inf);
+
+  return PyLong_FromLong (inf->inferior->pid);
+}
+
+static PyObject *
+infpy_get_was_attached (PyObject *self, void *closure)
+{
+  inferior_object *inf = (inferior_object *) self;
+  INFPY_REQUIRE_VALID (inf);
+  if (inf->inferior->attach_flag)
+    Py_RETURN_TRUE;
+  Py_RETURN_FALSE;
+}
+
+\f
+
+/* Implementation of gdb.inferiors () -> (gdb.Inferior, ...).
+   Returns a list of all inferiors.  */
+PyObject *
+gdbpy_inferiors (PyObject *unused, PyObject *unused2)
+{
+  int i;
+  struct inflist_entry *entry;
+  PyObject *tuple;
+
+  tuple = PyTuple_New (ninferiors);
+  if (!tuple)
+    return NULL;
+
+  /* The list is in reverse order of inferior age (i.e., newest comes
+     first).  */
+  for (i = 0, entry = gdbpy_inferior_list;
+       i < ninferiors;
+       i++, entry = entry->next)
+    {
+      Py_INCREF (entry->inf_obj);
+      PyTuple_SET_ITEM (tuple, i, (PyObject *) entry->inf_obj);
+    }
+
+  return tuple;
+}
+
+\f
+
+/* Membuf and memory manipulation.  */
+
+/* Implementation of gdb.read_memory (address, length).
+   Returns a Python buffer object with LENGTH bytes of the inferior's
+   memory at ADDRESS.  Both arguments are integers.  */
+static PyObject *
+infpy_read_memory (PyObject *self, PyObject *args)
+{
+  int error = 0;
+  CORE_ADDR addr, length;
+  void *buffer = NULL;
+  membuf_object *membuf_obj;
+  PyObject *addr_obj, *length_obj;
+  struct cleanup *cleanups;
+  volatile struct gdb_exception except;
+
+  if (! PyArg_ParseTuple (args, "OO", &addr_obj, &length_obj))
+    return NULL;
+
+  cleanups = make_cleanup (null_cleanup, NULL);
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      if (!get_addr_from_python (addr_obj, &addr)
+	  || !get_addr_from_python (length_obj, &length))
+	{
+	  error = 1;
+	  break;
+	}
+
+      buffer = xmalloc (length);
+      make_cleanup (xfree, buffer);
+
+      read_memory (addr, buffer, length);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  if (error)
+    {
+      do_cleanups (cleanups);
+      return NULL;
+    }
+
+  membuf_obj = PyObject_New (membuf_object, &membuf_object_type);
+  if (membuf_obj == NULL)
+    {
+      PyErr_SetString (PyExc_MemoryError,
+		       _("Could not allocate memory buffer object."));
+      do_cleanups (cleanups);
+      return NULL;
+    }
+
+  discard_cleanups (cleanups);
+
+  membuf_obj->buffer = buffer;
+  membuf_obj->addr = addr;
+  membuf_obj->length = length;
+
+  return PyBuffer_FromReadWriteObject ((PyObject *) membuf_obj, 0,
+				       Py_END_OF_BUFFER);
+}
+
+/* Implementation of gdb.write_memory (address, buffer [, length]).
+   Writes the contents of BUFFER (a Python object supporting the read
+   buffer protocol) at ADDRESS in the inferior's memory.  Write LENGTH
+   bytes from BUFFER, or its entire contents if the argument is not
+   provided.  The function returns nothing.  */
+static PyObject *
+infpy_write_memory (PyObject *self, PyObject *args)
+{
+  int buf_len, error = 0;
+  const char *buffer;
+  CORE_ADDR addr, length;
+  PyObject *addr_obj, *length_obj = NULL;
+  volatile struct gdb_exception except;
+
+  if (! PyArg_ParseTuple (args, "Os#|O", &addr_obj, &buffer, &buf_len,
+			  &length_obj))
+    return NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      if (!get_addr_from_python (addr_obj, &addr))
+	{
+	  error = 1;
+	  break;
+	}
+
+      if (!length_obj)
+	length = buf_len;
+      else if (!get_addr_from_python (length_obj, &length))
+	{
+	  error = 1;
+	  break;
+	}
+
+      write_memory (addr, buffer, length);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  if (error)
+    return NULL;
+
+  Py_RETURN_NONE;
+}
+
+/* Destructor of Membuf objects.  */
+static void
+mbpy_dealloc (PyObject *self)
+{
+  xfree (((membuf_object *) self)->buffer);
+  self->ob_type->tp_free (self);
+}
+
+/* Return a description of the Membuf object.  */
+static PyObject *
+mbpy_str (PyObject *self)
+{
+  membuf_object *membuf_obj = (membuf_object *) self;
+
+  return PyString_FromFormat (_("Memory buffer for address %s, \
+which is %s bytes long."),
+			      paddress (python_gdbarch, membuf_obj->addr),
+			      pulongest (membuf_obj->length));
+}
+
+static Py_ssize_t
+get_read_buffer (PyObject *self, Py_ssize_t segment, void **ptrptr)
+{
+  membuf_object *membuf_obj = (membuf_object *) self;
+
+  if (segment)
+    {
+      PyErr_SetString (PyExc_SystemError,
+		       _("The memory buffer supports only one segment."));
+      return -1;
+    }
+
+  *ptrptr = membuf_obj->buffer;
+
+  return membuf_obj->length;
+}
+
+static Py_ssize_t
+get_write_buffer (PyObject *self, Py_ssize_t segment, void **ptrptr)
+{
+  return get_read_buffer (self, segment, ptrptr);
+}
+
+static Py_ssize_t
+get_seg_count (PyObject *self, Py_ssize_t *lenp)
+{
+  if (lenp)
+    *lenp = ((membuf_object *) self)->length;
+
+  return 1;
+}
+
+static Py_ssize_t
+get_char_buffer (PyObject *self, Py_ssize_t segment, char **ptrptr)
+{
+  void *ptr = NULL;
+  Py_ssize_t ret;
+
+  ret = get_read_buffer (self, segment, &ptr);
+  *ptrptr = (char *) ptr;
+
+  return ret;
+}
+
+/* Adds GDB value V to the pattern buffer in *PATTERN_BUF.  If SIZE is
+   not zero, it specifies the number of bytes from V to copy to
+   *PATTERN_BUF.  The function increases the size of *PATTERN_BUF as
+   necessary, adjusting *PATTERN_BUF_END and *PATTERN_BUF_SIZE in the
+   process.  */
+static void
+add_value_pattern (struct value *v, int size, char **pattern_buf,
+		   char **pattern_buf_end, ULONGEST *pattern_buf_size)
+{
+  int val_bytes;
+
+  if (size)
+    {
+      LONGEST x = value_as_long (v);
+
+      if (size == 1)
+	*(*pattern_buf_end)++ = x;
+      else
+	{
+	  put_bits (x, *pattern_buf_end, size * 8,
+		    gdbarch_byte_order (python_gdbarch) == BFD_ENDIAN_BIG);
+	  *pattern_buf_end += size;
+	}
+    }
+  else
+   {
+     val_bytes = TYPE_LENGTH (value_type (v));
+
+     increase_pattern_buffer (pattern_buf, pattern_buf_end,
+			      pattern_buf_size, val_bytes);
+
+     memcpy (*pattern_buf_end, value_contents_raw (v), val_bytes);
+     *pattern_buf_end += val_bytes;
+   }
+}
+
+/* This function does the actual work of constructing the pattern
+   buffer from OBJ.  If OBJ is an object which implements the read
+   buffer protocol (such as a string, a byte array or gdb.Membuf),
+   then its contents are directly copied to *PATTERN_BUF.  If it is a
+   list, then this function is recursively called for each of its
+   elements.  If OBJ is an object which can be converted to a GDB
+   value, then the contents of the value are copied to PATTERN_BUF.
+   If SIZE is different than zero, then it limits the number of bytes
+   which are copied to the buffer in case OBJ is converted to a GDB
+   value.  That means that SIZE influences only Python scalars and
+   gdb.Value objects.  The function increases the size of *PATTERN_BUF
+   as necessary, adjusting *PATTERN_BUF_END and *PATTERN_BUF_SIZE in
+   the process.
+
+   Returns 1 on success or 0 on failure, with a Python exception set.
+   This function can also throw GDB exceptions.  */
+static int
+add_pattern_element (PyObject *obj, int size, char **pattern_buf,
+		     char **pattern_buf_end, ULONGEST *pattern_buf_size)
+{
+  if (PyObject_CheckReadBuffer (obj))
+    {
+      /* Handle string, Unicode string, byte array, gdb.Membuf and any
+         other object implementing the buffer protocol.  The SIZE
+         parameter is ignored in this case.  */
+      Py_ssize_t val_bytes;
+      const void *buffer;
+
+      if (PyObject_AsReadBuffer (obj, &buffer, &val_bytes) == -1)
+	return 0;
+
+      increase_pattern_buffer (pattern_buf, pattern_buf_end,
+			       pattern_buf_size, val_bytes);
+
+      memcpy (*pattern_buf_end, buffer, val_bytes);
+      *pattern_buf_end += val_bytes;
+    }
+  else if (gdbpy_is_value_object (obj))
+    add_value_pattern (value_object_to_value (obj), size, pattern_buf,
+		       pattern_buf_end, pattern_buf_size);
+  else if (PySequence_Check (obj))
+    {
+      /* Handle lists and tuples.  */
+      Py_ssize_t i, num_objs;
+
+      num_objs = PySequence_Size (obj);
+      for (i = 0; i < num_objs; i++)
+	if (!add_pattern_element (PySequence_GetItem (obj, i), size,
+				  pattern_buf, pattern_buf_end,
+				  pattern_buf_size))
+	  return 0;
+    }
+  else
+    {
+      /* See if we can convert from a Python object to a GDB value.  */
+      struct value *v = convert_value_from_python (obj);
+
+      if (v)
+	add_value_pattern (v, size, pattern_buf, pattern_buf_end,
+			   pattern_buf_size);
+      else
+	return 0;
+    }
+
+  return 1;
+}
+
+/* Constructs the search pattern from OBJ, putting it in
+   *PATTERN_BUFP, and its size in *PATTERN_LENP.  See the function
+   add_pattern_element to learn how the search pattern is obtained
+   from OBJ.
+
+   Returns 1 on success or 0 on failure, with a Python exception set.
+   This function can also throw GDB exceptions.  */
+static int
+get_search_pattern (PyObject *obj, int size, char **pattern_bufp,
+		    ULONGEST *pattern_lenp)
+{
+  /* Buffer to hold the search pattern.  */
+  char *pattern_buf;
+  /* Current size of search pattern buffer.
+     We realloc space as needed.  */
+  ULONGEST pattern_buf_size;
+  /* Pointer to one past the last in-use part of pattern_buf.  */
+  char *pattern_buf_end;
+  struct cleanup *old_cleanups;
+
+  allocate_pattern_buffer (&pattern_buf, &pattern_buf_end,
+			   &pattern_buf_size);
+  old_cleanups = make_cleanup (free_current_contents, &pattern_buf);
+
+  if (!add_pattern_element (obj, size, &pattern_buf, &pattern_buf_end,
+			    &pattern_buf_size))
+    {
+      do_cleanups (old_cleanups);
+
+      return 0;
+    }
+
+  *pattern_bufp = pattern_buf;
+  *pattern_lenp = pattern_buf_end - pattern_buf;
+
+  discard_cleanups (old_cleanups);
+
+  return 1;
+}
+
+/* Implementation of
+   gdb.search_memory (address, length, pattern [, size] [, max_count]).
+   The third argument may be either a pattern, or a list or tuple of
+   patterns to be searched.  Size is the size in bytes of each search
+   query value, either 1, 2, 4 or 8.  Returns a list of the addresses
+   where matches were found.  */
+static PyObject *
+infpy_search_memory (PyObject *self, PyObject *args, PyObject *kw)
+{
+  int size = 0;
+  unsigned int found_count = 0;
+  long max_count = 0;
+  CORE_ADDR start_addr, length;
+  char *pattern_buf;
+  static char *keywords[] = { "address", "length", "pattern", "size",
+			      "max_count", NULL };
+  ULONGEST pattern_len, search_space_len;
+  PyObject *pattern, *list = NULL, *start_addr_obj, *length_obj;
+  volatile struct gdb_exception except;
+
+  if (! PyArg_ParseTupleAndKeywords (args, kw, "OOO|il", keywords,
+				     &start_addr_obj, &length_obj, &pattern,
+				     &size, &max_count))
+    return NULL;
+
+  if (!max_count)
+    max_count = LONG_MAX;
+
+  if (size != 0 && size != 1 && size != 2 && size != 4 && size != 8)
+    {
+      PyErr_SetString (PyExc_ValueError, _("Invalid pattern size."));
+      return NULL;
+    }
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      if (get_addr_from_python (start_addr_obj, &start_addr)
+	  && get_addr_from_python (length_obj, &length))
+	{
+	  if (!length)
+	    {
+	      PyErr_SetString (PyExc_ValueError,
+			       _("Search range is empty."));
+	      break;
+	    }
+	  /* Watch for overflows.  */
+	  else if (length > CORE_ADDR_MAX
+		   || (start_addr + length - 1) < start_addr)
+	    {
+	      PyErr_SetString (PyExc_ValueError,
+			       _("The search range too large."));
+
+	      break;
+	    }
+
+	  search_space_len = length;
+
+	  if (get_search_pattern (pattern, size, &pattern_buf,
+				  &pattern_len))
+	    {
+	      /* Any cleanups get automatically executed on an
+		 exception.  */
+	      struct cleanup *cleanups = make_cleanup (xfree, pattern_buf);
+
+	      list = PyList_New (0);
+
+	      while (search_space_len >= pattern_len
+		     && found_count <  max_count)
+		{
+		  CORE_ADDR found_addr;
+		  int found;
+
+		  found = search_memory (&start_addr, &search_space_len,
+					 pattern_buf, pattern_len,
+					 &found_addr);
+		  if (found <= 0)
+		    break;
+
+		  PyList_Append (list, PyLong_FromUnsignedLong (found_addr));
+		  ++found_count;
+		}
+
+	      do_cleanups (cleanups);
+	    }
+	}
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  return list;
+}
+
+\f
+
+void
+gdbpy_initialize_inferior (void)
+{
+  if (PyType_Ready (&inferior_object_type) < 0)
+    return;
+
+  Py_INCREF (&inferior_object_type);
+  PyModule_AddObject (gdb_module, "Inferior",
+		      (PyObject *) &inferior_object_type);
+
+  gdbpy_inferior_list = NULL;
+  ninferiors = 0;
+
+  observer_attach_inferior_appeared (add_inferior_object);
+  observer_attach_inferior_exit (delete_inferior_object);
+  observer_attach_new_thread (add_thread_object);
+  observer_attach_thread_exit (delete_thread_object);
+
+  if (PyType_Ready (&membuf_object_type) < 0)
+    return;
+
+  Py_INCREF (&membuf_object_type);
+  PyModule_AddObject (gdb_module, "Membuf", (PyObject *) &membuf_object_type);
+}
+
+\f
+
+static PyGetSetDef inferior_object_getset[] =
+{
+  { "num", infpy_get_num, NULL, "ID of inferior, as assigned by GDB.", NULL },
+  { "pid", infpy_get_pid, NULL, "PID of inferior, as assigned by the OS.",
+    NULL },
+  { "was_attached", infpy_get_was_attached, NULL,
+    "True if the inferior was created using 'attach'.", NULL },
+
+  { NULL }
+};
+
+static PyMethodDef inferior_object_methods[] =
+{
+  { "threads", infpy_threads, METH_NOARGS,
+    "Return all the threads of this inferior." },
+  { "read_memory", infpy_read_memory, METH_VARARGS,
+    "read_memory (address, length) -> buffer\n\
+Return a buffer object for reading from the inferior's memory." },
+  { "write_memory", infpy_write_memory, METH_VARARGS,
+    "write_memory (address, buffer [, length])\n\
+Write the given buffer object to the inferior's memory." },
+  { "search_memory", (PyCFunction) infpy_search_memory,
+    METH_VARARGS | METH_KEYWORDS,
+    "search_memory (address, length, pattern [, size] [, max_count]) -> list\n\
+Return a list with the addresses where matches were found." },
+
+  { NULL }
+};
+
+static PyTypeObject inferior_object_type =
+{
+  PyObject_HEAD_INIT (NULL)
+  0,				  /* ob_size */
+  "gdb.Inferior",		  /* tp_name */
+  sizeof (inferior_object),	  /* tp_basicsize */
+  0,				  /* tp_itemsize */
+  0,				  /* tp_dealloc */
+  0,				  /* tp_print */
+  0,				  /* tp_getattr */
+  0,				  /* tp_setattr */
+  0,				  /* tp_compare */
+  0,				  /* tp_repr */
+  0,				  /* tp_as_number */
+  0,				  /* tp_as_sequence */
+  0,				  /* tp_as_mapping */
+  0,				  /* tp_hash  */
+  0,				  /* tp_call */
+  0,				  /* tp_str */
+  0,				  /* tp_getattro */
+  0,				  /* tp_setattro */
+  0,				  /* tp_as_buffer */
+  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER,  /* tp_flags */
+  "GDB inferior object",	  /* tp_doc */
+  0,				  /* tp_traverse */
+  0,				  /* tp_clear */
+  0,				  /* tp_richcompare */
+  0,				  /* tp_weaklistoffset */
+  0,				  /* tp_iter */
+  0,				  /* tp_iternext */
+  inferior_object_methods,	  /* tp_methods */
+  0,				  /* tp_members */
+  inferior_object_getset,	  /* tp_getset */
+  0,				  /* tp_base */
+  0,				  /* tp_dict */
+  0,				  /* tp_descr_get */
+  0,				  /* tp_descr_set */
+  0,				  /* tp_dictoffset */
+  0,				  /* tp_init */
+  0				  /* tp_alloc */
+};
+
+\f
+
+/* Python doesn't provide a decent way to get compatibility here.  */
+#if HAVE_LIBPYTHON2_4
+#define CHARBUFFERPROC_NAME getcharbufferproc
+#else
+#define CHARBUFFERPROC_NAME charbufferproc
+#endif
+
+static PyBufferProcs buffer_procs = {
+  get_read_buffer,
+  get_write_buffer,
+  get_seg_count,
+  /* The cast here works around a difference between Python 2.4 and
+     Python 2.5.  */
+  (CHARBUFFERPROC_NAME) get_char_buffer
+};
+
+static PyTypeObject membuf_object_type = {
+  PyObject_HEAD_INIT (NULL)
+  0,				  /*ob_size*/
+  "gdb.Membuf",			  /*tp_name*/
+  sizeof (membuf_object),	  /*tp_basicsize*/
+  0,				  /*tp_itemsize*/
+  mbpy_dealloc,			  /*tp_dealloc*/
+  0,				  /*tp_print*/
+  0,				  /*tp_getattr*/
+  0,				  /*tp_setattr*/
+  0,				  /*tp_compare*/
+  0,				  /*tp_repr*/
+  0,				  /*tp_as_number*/
+  0,				  /*tp_as_sequence*/
+  0,				  /*tp_as_mapping*/
+  0,				  /*tp_hash */
+  0,				  /*tp_call*/
+  mbpy_str,			  /*tp_str*/
+  0,				  /*tp_getattro*/
+  0,				  /*tp_setattro*/
+  &buffer_procs,		  /*tp_as_buffer*/
+  Py_TPFLAGS_DEFAULT,		  /*tp_flags*/
+  "GDB memory buffer object", 	  /*tp_doc*/
+  0,				  /* tp_traverse */
+  0,				  /* tp_clear */
+  0,				  /* tp_richcompare */
+  0,				  /* tp_weaklistoffset */
+  0,				  /* tp_iter */
+  0,				  /* tp_iternext */
+  0,				  /* tp_methods */
+  0,				  /* tp_members */
+  0,				  /* tp_getset */
+  0,				  /* tp_base */
+  0,				  /* tp_dict */
+  0,				  /* tp_descr_get */
+  0,				  /* tp_descr_set */
+  0,				  /* tp_dictoffset */
+  0,				  /* tp_init */
+  0,				  /* tp_alloc */
+  PyType_GenericNew		  /* tp_new */
+};
diff --git a/gdb/python/py-infthread.c b/gdb/python/py-infthread.c
new file mode 100644
index 0000000..d2e8109
--- /dev/null
+++ b/gdb/python/py-infthread.c
@@ -0,0 +1,371 @@
+/* Python interface to inferior threads.
+
+   Copyright (C) 2009, 2010 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 "exceptions.h"
+#include "gdbthread.h"
+#include "inferior.h"
+#include "python-internal.h"
+
+static PyTypeObject thread_object_type;
+
+/* Require that INFERIOR be a valid inferior ID.  */
+#define THPY_REQUIRE_VALID(Thread)				\
+  do {								\
+    if (!Thread->thread)					\
+      {								\
+	PyErr_SetString (PyExc_RuntimeError,			\
+			 _("Thread no longer exists."));	\
+	return NULL;						\
+      }								\
+  } while (0)
+
+\f
+
+thread_object *
+create_thread_object (struct thread_info *tp)
+{
+  thread_object *thread_obj;
+
+  thread_obj = PyObject_New (thread_object, &thread_object_type);
+  if (!thread_obj)
+    return NULL;
+
+  thread_obj->thread = tp;
+  thread_obj->inf_obj = find_inferior_object (PIDGET (tp->ptid));
+  Py_INCREF (thread_obj->inf_obj);
+
+  return thread_obj;
+}
+
+\f
+
+static void
+thpy_dealloc (PyObject *self)
+{
+  Py_DECREF (((thread_object *) self)->inf_obj);
+  self->ob_type->tp_free (self);
+}
+
+static PyObject *
+thpy_get_num (PyObject *self, void *closure)
+{
+  thread_object *thread_obj = (thread_object *) self;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  return PyLong_FromLong (thread_obj->thread->num);
+}
+
+/* Getter for InferiorThread.ptid  -> (pid, lwp, tid).
+   Returns a tuple with the thread's ptid components.  */
+static PyObject *
+thpy_get_ptid (PyObject *self, void *closure)
+{
+  int pid;
+  long tid, lwp;
+  thread_object *thread_obj = (thread_object *) self;
+  PyObject *ret;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  ret = PyTuple_New (3);
+  if (!ret)
+    return NULL;
+
+  pid = ptid_get_pid (thread_obj->thread->ptid);
+  lwp = ptid_get_lwp (thread_obj->thread->ptid);
+  tid = ptid_get_tid (thread_obj->thread->ptid);
+
+  PyTuple_SET_ITEM (ret, 0, PyInt_FromLong (pid));
+  PyTuple_SET_ITEM (ret, 1, PyInt_FromLong (lwp));
+  PyTuple_SET_ITEM (ret, 2, PyInt_FromLong (tid));
+
+  return ret;
+}
+
+\f
+
+/* Implementation of Inferior.frames () -> (gdb.Frame, ...).
+   Returns a tuple of all frame objects.  */
+PyObject *
+thpy_frames (PyObject *self, PyObject *args)
+{
+  int result = 0;
+  struct frame_info *frame;
+  PyObject *frame_obj;
+  PyObject *list, *tuple;
+  thread_object *thread_obj = (thread_object *) self;
+  struct cleanup *cleanup;
+  volatile struct gdb_exception except;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  list = PyList_New (0);
+  if (list == NULL)
+    {
+      PyErr_SetString (PyExc_MemoryError,
+		       _("Could not allocate frames list."));
+      return NULL;
+    }
+
+  cleanup = make_cleanup_restore_current_thread ();
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      switch_to_thread (thread_obj->thread->ptid);
+
+      for (frame = get_current_frame (); frame;
+	   frame = get_prev_frame (frame))
+	{
+	  frame_obj = frame_info_to_frame_object (frame);
+	  if (frame_obj == NULL)
+	    {
+	      Py_DECREF (list);
+	      list = NULL;
+	      break;
+	    }
+
+	  PyList_Append (list, frame_obj);
+	}
+    }
+  if (except.reason < 0)
+    {
+      Py_DECREF (list);
+      return PyErr_Format (except.reason == RETURN_QUIT
+			   ? PyExc_KeyboardInterrupt : PyExc_RuntimeError,
+			   "%s", except.message);
+    }
+
+  do_cleanups (cleanup);
+
+  if (list)
+    {
+      tuple = PyList_AsTuple (list);
+      Py_DECREF (list);
+    }
+  else
+    tuple = NULL;
+
+  return tuple;
+}
+
+/* Implementation of InferiorThread.newest_frame () -> gdb.Frame.
+   Returns the newest frame object.  */
+PyObject *
+thpy_newest_frame (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame;
+  PyObject *frame_obj = NULL;   /* Initialize to appease gcc warning.  */
+  thread_object *thread_obj = (thread_object *) self;
+  struct cleanup *cleanup;
+  volatile struct gdb_exception except;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  cleanup = make_cleanup_restore_current_thread ();
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      switch_to_thread (thread_obj->thread->ptid);
+
+      frame = get_current_frame ();
+      frame_obj = frame_info_to_frame_object (frame);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  do_cleanups (cleanup);
+
+  return frame_obj;
+}
+
+/* Implementation of InferiorThread.switch ().
+   Makes this the GDB selected thread.  */
+static PyObject *
+thpy_switch (PyObject *self, PyObject *args)
+{
+  thread_object *thread_obj = (thread_object *) self;
+  struct cleanup *cleanup;
+  volatile struct gdb_exception except;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      switch_to_thread (thread_obj->thread->ptid);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  Py_RETURN_NONE;
+}
+
+/* Implementation of InferiorThread.is_stopped () -> Boolean.
+   Return whether the thread is stopped.  */
+static PyObject *
+thpy_is_stopped (PyObject *self, PyObject *args)
+{
+  thread_object *thread_obj = (thread_object *) self;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  if (is_stopped (thread_obj->thread->ptid))
+    Py_RETURN_TRUE;
+
+  Py_RETURN_FALSE;
+}
+
+/* Implementation of InferiorThread.is_running () -> Boolean.
+   Return whether the thread is running.  */
+static PyObject *
+thpy_is_running (PyObject *self, PyObject *args)
+{
+  thread_object *thread_obj = (thread_object *) self;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  if (is_running (thread_obj->thread->ptid))
+    Py_RETURN_TRUE;
+
+  Py_RETURN_FALSE;
+}
+
+/* Implementation of InferiorThread.is_exited () -> Boolean.
+   Return whether the thread is exited.  */
+static PyObject *
+thpy_is_exited (PyObject *self, PyObject *args)
+{
+  thread_object *thread_obj = (thread_object *) self;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  if (is_exited (thread_obj->thread->ptid))
+    Py_RETURN_TRUE;
+
+  Py_RETURN_FALSE;
+}
+
+\f
+
+/* Implementation of gdb.selected_thread () -> gdb.InferiorThread.
+   Returns the selected thread object.  */
+PyObject *
+gdbpy_selected_thread (PyObject *self, PyObject *args)
+{
+  PyObject *thread_obj;
+
+  thread_obj = (PyObject *) find_thread_object (inferior_ptid);
+  if (thread_obj)
+    {
+      Py_INCREF (thread_obj);
+      return thread_obj;
+    }
+
+  Py_RETURN_NONE;
+}
+
+\f
+
+void
+gdbpy_initialize_thread (void)
+{
+
+  if (PyType_Ready (&thread_object_type) < 0)
+    return;
+
+  Py_INCREF (&thread_object_type);
+  PyModule_AddObject (gdb_module, "InferiorThread",
+		      (PyObject *) &thread_object_type);
+}
+
+\f
+
+static PyGetSetDef thread_object_getset[] =
+{
+  { "num", thpy_get_num, NULL, "ID of the thread, as assigned by GDB.", NULL },
+  { "ptid", thpy_get_ptid, NULL, "ID of the thread, as assigned by the OS.",
+    NULL },
+
+  { NULL }
+};
+
+static PyMethodDef thread_object_methods[] =
+{
+  { "frames", thpy_frames, METH_NOARGS,
+    "frames () -> (gdb.Frame, ...)\n\
+Return a tuple containing all frames in the thread." },
+  { "newest_frame", thpy_newest_frame, METH_NOARGS,
+    "newest_frame () -> gdb.Frame\n\
+Return the newest frame in the thread." },
+  { "switch", thpy_switch, METH_NOARGS,
+    "switch ()\n\
+Makes this the GDB selected thread." },
+  { "is_stopped", thpy_is_stopped, METH_NOARGS,
+    "is_stopped () -> Boolean\n\
+Return whether the thread is stopped." },
+  { "is_running", thpy_is_running, METH_NOARGS,
+    "is_running () -> Boolean\n\
+Return whether the thread is running." },
+  { "is_exited", thpy_is_exited, METH_NOARGS,
+    "is_exited () -> Boolean\n\
+Return whether the thread is exited." },
+
+  { NULL }
+};
+
+static PyTypeObject thread_object_type =
+{
+  PyObject_HEAD_INIT (NULL)
+  0,				  /*ob_size*/
+  "gdb.InferiorThread",		  /*tp_name*/
+  sizeof (thread_object),	  /*tp_basicsize*/
+  0,				  /*tp_itemsize*/
+  thpy_dealloc,			  /*tp_dealloc*/
+  0,				  /*tp_print*/
+  0,				  /*tp_getattr*/
+  0,				  /*tp_setattr*/
+  0,				  /*tp_compare*/
+  0,				  /*tp_repr*/
+  0,				  /*tp_as_number*/
+  0,				  /*tp_as_sequence*/
+  0,				  /*tp_as_mapping*/
+  0,				  /*tp_hash */
+  0,				  /*tp_call*/
+  0,				  /*tp_str*/
+  0,				  /*tp_getattro*/
+  0,				  /*tp_setattro*/
+  0,				  /*tp_as_buffer*/
+  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER,  /*tp_flags*/
+  "GDB thread object",		  /* tp_doc */
+  0,				  /* tp_traverse */
+  0,				  /* tp_clear */
+  0,				  /* tp_richcompare */
+  0,				  /* tp_weaklistoffset */
+  0,				  /* tp_iter */
+  0,				  /* tp_iternext */
+  thread_object_methods,	  /* tp_methods */
+  0,				  /* tp_members */
+  thread_object_getset,		  /* tp_getset */
+  0,				  /* tp_base */
+  0,				  /* tp_dict */
+  0,				  /* tp_descr_get */
+  0,				  /* tp_descr_set */
+  0,				  /* tp_dictoffset */
+  0,				  /* tp_init */
+  0				  /* tp_alloc */
+};
diff --git a/gdb/python/py-utils.c b/gdb/python/py-utils.c
index cd11834..0010248 100644
--- a/gdb/python/py-utils.c
+++ b/gdb/python/py-utils.c
@@ -19,6 +19,7 @@
 
 #include "defs.h"
 #include "charset.h"
+#include "value.h"
 #include "python-internal.h"
 
 
@@ -222,3 +223,51 @@ gdbpy_is_string (PyObject *obj)
 {
   return PyString_Check (obj) || PyUnicode_Check (obj);
 }
+
+/* Converts OBJ to a CORE_ADDR value.
+
+   Returns 1 on success or 0 on failure, with a Python exception set.  This
+   function can also throw GDB exceptions.
+*/
+
+int
+get_addr_from_python (PyObject *obj, CORE_ADDR *addr)
+{
+  if (gdbpy_is_value_object (obj))
+    *addr = value_as_address (value_object_to_value (obj));
+  else if (PyLong_Check (obj))
+    {
+      /* Assume CORE_ADDR corresponds to unsigned long.  */
+      *addr = PyLong_AsUnsignedLong (obj);
+      if (PyErr_Occurred () != NULL)
+	return 0;
+    }
+  else if (PyInt_Check (obj))
+    {
+      long val;
+
+      /* Assume CORE_ADDR corresponds to unsigned long.  */
+      val = PyInt_AsLong (obj);
+
+      if (val >= 0)
+	*addr = val;
+      else
+      {
+	/* If no error ocurred, VAL is indeed negative.  */
+	if (PyErr_Occurred () != NULL)
+	  return 0;
+
+	PyErr_SetString (PyExc_ValueError,
+			 _("Supplied address is negative."));
+	return 0;
+      }
+    }
+  else
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       _("Invalid type for address."));
+      return 0;
+    }
+
+  return 1;
+}
diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c
index eb20de8..2024021 100644
--- a/gdb/python/py-value.c
+++ b/gdb/python/py-value.c
@@ -43,6 +43,10 @@
 /* Python's long type corresponds to C's long long type.  */
 #define builtin_type_pylong builtin_type (python_gdbarch)->builtin_long_long
 
+/* Python's long type corresponds to C's long long type.  Unsigned version.  */
+#define builtin_type_upylong builtin_type \
+  (python_gdbarch)->builtin_unsigned_long_long
+
 #define builtin_type_pybool \
   language_bool_type (python_language, python_gdbarch)
 
@@ -961,7 +965,34 @@ convert_value_from_python (PyObject *obj)
 	{
 	  LONGEST l = PyLong_AsLongLong (obj);
 
-	  if (! PyErr_Occurred ())
+	  if (PyErr_Occurred ())
+	    {
+	      /* If the error was an overflow, we can try converting to
+	         ULONGEST instead.  */
+	      if (PyErr_ExceptionMatches (PyExc_OverflowError))
+		{
+		  PyObject *etype, *evalue, *etraceback, *zero;
+
+		  PyErr_Fetch (&etype, &evalue, &etraceback);
+		  zero = PyInt_FromLong (0);
+
+		  /* Check whether obj is positive.  */
+		  if (PyObject_RichCompareBool (obj, zero, Py_GT) > 0)
+		    {
+		      ULONGEST ul;
+
+		      ul = PyLong_AsUnsignedLongLong (obj);
+		      if (! PyErr_Occurred ())
+			value = value_from_ulongest (builtin_type_upylong, ul);
+		    }
+		  else
+		    /* There's nothing we can do.  */
+		    PyErr_Restore (etype, evalue, etraceback);
+
+		  Py_DECREF (zero);
+		}
+	    }
+	  else
 	    value = value_from_longest (builtin_type_pylong, l);
 	}
       else if (PyFloat_Check (obj))
@@ -1028,6 +1059,14 @@ gdbpy_history (PyObject *self, PyObject *args)
   return value_to_value_object (res_val);
 }
 
+/* Returns 1 in OBJ is a gdb.Value object, 0 otherwise.  */
+
+int
+gdbpy_is_value_object (PyObject *obj)
+{
+  return PyObject_TypeCheck (obj, &value_object_type);
+}
+
 void
 gdbpy_initialize_values (void)
 {
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index f41f32a..a5c2aba 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -80,6 +80,17 @@ extern PyTypeObject value_object_type;
 extern PyTypeObject block_object_type;
 extern PyTypeObject symbol_object_type;
 
+typedef struct
+{
+  PyObject_HEAD
+
+  /* The thread we represent.  */
+  struct thread_info *thread;
+
+  /* The Inferior object to which this thread belongs.  */
+  PyObject *inf_obj;
+} thread_object;
+
 extern struct cmd_list_element *set_python_list;
 extern struct cmd_list_element *show_python_list;
 
@@ -91,7 +102,11 @@ PyObject *gdbpy_selected_frame (PyObject *self, PyObject *args);
 PyObject *gdbpy_block_for_pc (PyObject *self, PyObject *args);
 PyObject *gdbpy_lookup_type (PyObject *self, PyObject *args, PyObject *kw);
 PyObject *gdbpy_create_lazy_string_object (CORE_ADDR address, long length,
-					   const char *encoding, struct type *type);
+					   const char *encoding,
+					   struct type *type);
+PyObject *gdbpy_inferiors (PyObject *unused, PyObject *unused2);
+PyObject *gdbpy_selected_thread (PyObject *self, PyObject *args);
+
 PyObject *gdbpy_get_hook_function (const char *);
 PyObject *gdbpy_parameter (PyObject *self, PyObject *args);
 PyObject *gdbpy_parameter_value (enum var_types type, void *var);
@@ -105,6 +120,7 @@ PyObject *symbol_to_symbol_object (struct symbol *sym);
 PyObject *block_to_block_object (struct block *block, struct objfile *objfile);
 PyObject *value_to_value_object (struct value *v);
 PyObject *type_to_type_object (struct type *);
+PyObject *frame_info_to_frame_object (struct frame_info *frame);
 
 PyObject *pspace_to_pspace_object (struct program_space *);
 PyObject *pspy_get_printers (PyObject *, void *);
@@ -112,6 +128,10 @@ PyObject *pspy_get_printers (PyObject *, void *);
 PyObject *objfile_to_objfile_object (struct objfile *);
 PyObject *objfpy_get_printers (PyObject *, void *);
 
+thread_object *create_thread_object (struct thread_info *tp);
+thread_object *find_thread_object (ptid_t ptid);
+PyObject *find_inferior_object (int pid);
+
 struct block *block_object_to_block (PyObject *obj);
 struct symbol *symbol_object_to_symbol (PyObject *obj);
 struct value *value_object_to_value (PyObject *self);
@@ -135,6 +155,8 @@ void gdbpy_initialize_objfile (void);
 void gdbpy_initialize_breakpoints (void);
 void gdbpy_initialize_lazy_string (void);
 void gdbpy_initialize_parameters (void);
+void gdbpy_initialize_thread (void);
+void gdbpy_initialize_inferior (void);
 
 struct cleanup *make_cleanup_py_decref (PyObject *py);
 
@@ -184,6 +206,8 @@ gdb_byte *gdbpy_extract_lazy_string (PyObject *string,
 				     struct type **str_type, 
 				     long *length, char **encoding);
 
+int gdbpy_is_value_object (PyObject *obj);
+
 /* Note that these are declared here, and not in python.h with the
    other pretty-printer functions, because they refer to PyObject.  */
 PyObject *apply_varobj_pretty_printer (PyObject *print_obj,
@@ -197,4 +221,6 @@ extern PyObject *gdbpy_children_cst;
 extern PyObject *gdbpy_to_string_cst;
 extern PyObject *gdbpy_display_hint_cst;
 
+int get_addr_from_python (PyObject *obj, CORE_ADDR *addr);
+
 #endif /* GDB_PYTHON_INTERNAL_H */
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 3110328..9847bf7 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -28,6 +28,8 @@
 #include "value.h"
 #include "language.h"
 #include "exceptions.h"
+#include "inferior.h"
+#include "gdbthread.h"
 
 #include <ctype.h>
 
@@ -669,6 +671,8 @@ Enables or disables printing of Python stack traces."),
   gdbpy_initialize_objfile ();
   gdbpy_initialize_breakpoints ();
   gdbpy_initialize_lazy_string ();
+  gdbpy_initialize_thread ();
+  gdbpy_initialize_inferior ();
 
   PyRun_SimpleString ("import gdb");
   PyRun_SimpleString ("gdb.pretty_printers = []");
@@ -775,7 +779,12 @@ Return the name of the current target wide charset." },
     "Write a string using gdb's filtered stream." },
   { "flush", gdbpy_flush, METH_NOARGS,
     "Flush gdb's filtered stdout stream." },
-
+  { "selected_thread", gdbpy_selected_thread, METH_NOARGS,
+    "selected_thread () -> gdb.InferiorThread.\n\
+Return the selected thread object." },
+  { "inferiors", gdbpy_inferiors, METH_NOARGS,
+    "inferiors () -> (gdb.Inferior, ...).\n\
+Return a tuple containing all inferiors." },
   {NULL, NULL, 0, NULL}
 };
 
diff --git a/gdb/target.h b/gdb/target.h
index 6f4b660..00fa2df 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -1378,6 +1378,20 @@ extern int target_search_memory (CORE_ADDR start_addr,
 #define target_get_tib_address(ptid, addr) \
   (*current_target.to_get_tib_address) ((ptid), (addr))
 
+/* Utility functions which can be used by search_memory implementations.  */
+
+void allocate_pattern_buffer (char **pattern_bufp, char **pattern_buf_end,
+			      ULONGEST *pattern_buf_size);
+
+void increase_pattern_buffer (char **pattern_bufp, char **pattern_buf_end,
+			      ULONGEST *pattern_buf_size, int val_bytes);
+
+int search_memory (CORE_ADDR *start_addr, ULONGEST *search_space_len,
+		   const char *pattern_buf, ULONGEST pattern_len,
+		   CORE_ADDR *found_addr);
+
+void put_bits (bfd_uint64_t data, char *buf, int bits, bfd_boolean big_p);
+
 /* Command logging facility.  */
 
 #define target_log_command(p)						\
diff --git a/gdb/testsuite/gdb.python/Makefile.in b/gdb/testsuite/gdb.python/Makefile.in
index c6a2678..660941d 100644
--- a/gdb/testsuite/gdb.python/Makefile.in
+++ b/gdb/testsuite/gdb.python/Makefile.in
@@ -2,7 +2,7 @@ VPATH = @srcdir@
 srcdir = @srcdir@
 
 EXECUTABLES = py-type py-value py-prettyprint py-template py-block \
-		py-symbol py-mi py-breakpoint
+		py-symbol py-mi py-breakpoint py-inferior py-infthread
 
 all info install-info dvi install uninstall installcheck check:
 	@echo "Nothing to be done for $@..."
diff --git a/gdb/testsuite/gdb.python/py-inferior.c b/gdb/testsuite/gdb.python/py-inferior.c
new file mode 100644
index 0000000..0b48299
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-inferior.c
@@ -0,0 +1,49 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.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;
+
+
+int f2 (int a)
+{
+  char *str = "hello, testsuite";
+
+  puts (str);	/* Break here.  */
+
+  return ++a;
+}
+
+int f1 (int a, int b)
+{
+  return f2(a) + b;
+}
+
+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 (int argc, char *argv[])
+{
+  init_bufs ();
+
+  return f1 (1, 2);
+}
diff --git a/gdb/testsuite/gdb.python/py-inferior.exp b/gdb/testsuite/gdb.python/py-inferior.exp
new file mode 100644
index 0000000..27f91a7
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-inferior.exp
@@ -0,0 +1,202 @@
+# Copyright (C) 2009, 2010 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 file is part of the GDB testsuite.  It tests the mechanism
+# exposing inferiors to Python.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+# Run a command in GDB, and report a failure if a Python exception is thrown.
+# If report_pass is true, report a pass if no exception is thrown.
+proc gdb_py_test_silent_cmd {cmd name report_pass} {
+    global gdb_prompt
+
+  gdb_test_multiple $cmd $name {
+      -re "Traceback.*$gdb_prompt $"  { fail $name }
+      -re "$gdb_prompt $"	      { if $report_pass { pass $name } }
+  }
+}
+
+set testfile "py-inferior"
+set srcfile ${testfile}.c
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
+    return -1
+}
+
+# Start with a fresh gdb.
+clean_restart ${testfile}
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+# The following tests require execution.
+
+if ![runto_main] then {
+    fail "Can't run to main"
+    return 0
+}
+
+runto [gdb_get_line_number "Break here."]
+
+# Test basic gdb.Inferior attributes and methods.
+
+gdb_py_test_silent_cmd "python inferiors = gdb.inferiors ()" "get inferiors list" 1
+gdb_test "python print inferiors" "\\(<gdb.Inferior object at 0x\[\[:xdigit:\]\]+>,\\)" "verify inferiors list"
+gdb_py_test_silent_cmd "python i0 = inferiors\[0\]" "get first inferior" 0
+
+gdb_test "python print 'result =', i0 == inferiors\[0\]" " = True" "test equality comparison (true)"
+gdb_test "python print 'result =', i0.num" " = \[0-9\]+" "test Inferior.num"
+gdb_test "python print 'result =', i0.pid" " = \[0-9\]+" "test Inferior.pid"
+gdb_test "python print 'result =', i0.was_attached" " = False" "test Inferior.was_attached"
+gdb_test "python print i0.threads ()" "\\(<gdb.InferiorThread object at 0x\[\[:xdigit:\]\]+>,\\)" "test Inferior.threads"
+
+# Test memory read and write operations.
+
+gdb_py_test_silent_cmd "python addr = gdb.selected_frame ().read_var ('str')" \
+  "read str address" 0
+gdb_py_test_silent_cmd "python str = gdb.inferiors()\[0\].read_memory (addr, 5)" \
+  "read str contents" 1
+gdb_py_test_silent_cmd "python str\[1\] = 'a'" "change str" 0
+gdb_py_test_silent_cmd "python gdb.inferiors()\[0\].write_memory (addr, str)" \
+  "write str" 1
+gdb_test "print str" " = 0x\[\[:xdigit:\]\]+ \"hallo, testsuite\"" \
+  "ensure str was changed in the inferior"
+
+# Test memory search.
+
+set hex_number {0x[0-9a-fA-F][0-9a-fA-F]*}
+set dec_number {[0-9]+}
+set history_prefix {[$][0-9]* = }
+set newline {[\r\n]+}
+set pattern_not_found "${newline}.]"
+set one_pattern_found "${newline}.${dec_number}L]"
+set two_patterns_found "${newline}.${dec_number}L, ${dec_number}L]"
+
+# Test string pattern.
+
+gdb_test "set *(int32_t*) &int8_search_buf\[10\] = 0x61616161" "" ""
+gdb_test "py search_buf = gdb.selected_frame ().read_var ('int8_search_buf')" "" ""
+gdb_test "py start_addr = search_buf.address" "" ""
+gdb_test "py length = search_buf.type.sizeof" "" ""
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, 'aaa')" \
+  "${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 "py print gdb.inferiors()\[0\].search_memory (start_addr, 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 "py print gdb.inferiors()\[0\].search_memory (start_addr, 10+3+1, \['a', 'a', 'a', 'a'\])" \
+  "${one_pattern_found}" "pattern found at end of range"
+
+# Test max-count with size, with different parameter position
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, \[0x61, 0x61\], 1, 1)" \
+  "${one_pattern_found}" "size = 1, max_count = 1"
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, \[0x61, 0x61\], 1, 2)" \
+  "${two_patterns_found}" "size = 1, max_count = 2, normal ordering"
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, \[0x61, 0x61\], size = 1, max_count = 2)" \
+  "${two_patterns_found}" "size = 1, max_count = 2, normal ordering, with keywords"
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, \[0x61, 0x61\], max_count = 2, size = 1)" \
+  "${two_patterns_found}" "size = 1, max_count = 2, inverted ordering"
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, \['a', 'a'\], max_count = 2)" \
+  "${two_patterns_found}" "max_count = 2, with keyword"
+
+# Test 16-bit pattern.
+
+gdb_test "set int16_search_buf\[10\] = 0x1234" "" ""
+gdb_test "py search_buf = gdb.selected_frame ().read_var ('int16_search_buf')" "" ""
+gdb_test "py start_addr = search_buf.address" "" ""
+gdb_test "py length = search_buf.type.sizeof" "" ""
+gdb_test "py pattern = gdb.parse_and_eval ('(int16_t) 0x1234')" "" ""
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, 0x1234, 2)" \
+  "${one_pattern_found}" "find 16-bit pattern, with python pattern"
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, pattern)" \
+  "${one_pattern_found}" "find 16-bit pattern, with value pattern"
+
+# Test 32-bit pattern.
+
+gdb_test "set int32_search_buf\[10\] = 0x12345678" "" ""
+gdb_test "py search_buf = gdb.selected_frame ().read_var ('int32_search_buf')" "" ""
+gdb_test "py start_addr = search_buf.address" "" ""
+gdb_test "py length = search_buf.type.sizeof" "" ""
+gdb_test "py pattern = gdb.parse_and_eval ('(int32_t) 0x12345678')" "" ""
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, 0x12345678, 4)" \
+  "${one_pattern_found}" "find 32-bit pattern, with python pattern"
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, pattern)" \
+  "${one_pattern_found}" "find 32-bit pattern, with value pattern"
+
+# Test 64-bit pattern.
+
+gdb_test "set int64_search_buf\[10\] = 0xfedcba9876543210LL" "" ""
+gdb_test "py search_buf = gdb.selected_frame ().read_var ('int64_search_buf')" "" ""
+gdb_test "py start_addr = search_buf.address" "" ""
+gdb_test "py length = search_buf.type.sizeof" "" ""
+gdb_test "py pattern = gdb.parse_and_eval ('(int64_t) 0xfedcba9876543210LL')" "" ""
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, 0xfedcba9876543210, 8)" \
+  "${one_pattern_found}" "find 64-bit pattern, with python pattern"
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, pattern)" \
+  "${one_pattern_found}" "find 64-bit pattern, with value 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 "py search_buf = gdb.selected_frame ().read_var ('search_buf')" "" ""
+gdb_test "py start_addr = search_buf\[0\].address" "" ""
+gdb_test "py pattern1 = gdb.parse_and_eval ('(int8_t) 0x62')" "" ""
+gdb_test "py pattern2 = gdb.parse_and_eval ('(int16_t) 0x6363')" "" ""
+gdb_test "py pattern3 = gdb.parse_and_eval ('(int32_t) 0x64646464')" "" ""
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, 100, \[pattern1, pattern2, pattern3\])" \
+    "${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 ;
+
+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 "py start_addr = gdb.selected_frame ().read_var ('search_buf')" "" ""
+gdb_test "py length = gdb.selected_frame ().read_var ('search_buf_size')" "" ""
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, 0x12345678, 4)" \
+  "${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 "py print gdb.inferiors()\[0\].search_memory (start_addr, length, 0xfdb97531, 4)" \
+      "${one_pattern_found}" "find pattern straddling chunk boundary"
+}
diff --git a/gdb/testsuite/gdb.python/py-infthread.c b/gdb/testsuite/gdb.python/py-infthread.c
new file mode 100644
index 0000000..22eb9f2
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-infthread.c
@@ -0,0 +1,14 @@
+int f2 (int a)
+{
+  return ++a;
+}
+
+int f1 (int a, int b)
+{
+  return f2(a) + b;
+}
+
+int main (int argc, char *argv[])
+{
+  return f1 (1, 2);
+}
diff --git a/gdb/testsuite/gdb.python/py-infthread.exp b/gdb/testsuite/gdb.python/py-infthread.exp
new file mode 100644
index 0000000..a25d0b5
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-infthread.exp
@@ -0,0 +1,53 @@
+# Copyright (C) 2009, 2010 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 file is part of the GDB testsuite.  It tests the mechanism
+# exposing inferior threads to Python.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+set testfile "py-infthread"
+set srcfile ${testfile}.c
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
+    return -1
+}
+
+# Start with a fresh gdb.
+clean_restart ${testfile}
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+# The following tests require execution.
+
+if ![runto_main] then {
+    fail "Can't run to main"
+    return 0
+}
+
+runto [gdb_get_line_number "Break here."]
+
+# Test basic gdb.Inferior attributes and methods.
+
+gdb_py_test_silent_cmd "python t0 = gdb.selected_thread ()" "test gdb.selected_thread" 1
+gdb_test "python print t0" "\\<gdb.InferiorThread object at 0x\[\[:xdigit:\]\]+>" "verify InferiorThread object"
+gdb_test "python print 'result =', t0.num" " = \[0-9\]+" "test Inferior.num"
+gdb_test "python print 'result =', t0.ptid" " = \\(\[0-9\]+, \[0-9\]+, \[0-9\]+\\)" "test InferiorThread.ptid"
+
+gdb_test "python print 'result =', t0.is_stopped ()" " = True" "test InferiorThread.is_stopped"
+gdb_test "python print 'result =', t0.is_running ()" " = False" "test InferiorThread.is_running"
+gdb_test "python print 'result =', t0.is_exited ()" " = False" "test InferiorThread.is_exited"
diff --git a/gdb/value.c b/gdb/value.c
index ec33403..59def1a 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -2203,6 +2203,43 @@ pack_long (gdb_byte *buf, struct type *type, LONGEST num)
 }
 
 
+/* Pack NUM into BUF using a target format of TYPE.  */
+
+void
+pack_unsigned_long (gdb_byte *buf, struct type *type, ULONGEST num)
+{
+  int len;
+  enum bfd_endian byte_order;
+
+  type = check_typedef (type);
+  len = TYPE_LENGTH (type);
+  byte_order = gdbarch_byte_order (get_type_arch (type));
+
+  switch (TYPE_CODE (type))
+    {
+    case TYPE_CODE_INT:
+    case TYPE_CODE_CHAR:
+    case TYPE_CODE_ENUM:
+    case TYPE_CODE_FLAGS:
+    case TYPE_CODE_BOOL:
+    case TYPE_CODE_RANGE:
+    case TYPE_CODE_MEMBERPTR:
+      store_unsigned_integer (buf, len, byte_order, num);
+      break;
+
+    case TYPE_CODE_REF:
+    case TYPE_CODE_PTR:
+      store_typed_address (buf, type, (CORE_ADDR) num);
+      break;
+
+    default:
+      error (_("\
+Unexpected type (%d) encountered for unsigned integer constant."),
+	     TYPE_CODE (type));
+    }
+}
+
+
 /* Convert C numbers into newly allocated values.  */
 
 struct value *
@@ -2215,6 +2252,19 @@ value_from_longest (struct type *type, LONGEST num)
 }
 
 
+/* Convert C unsigned numbers into newly allocated values.  */
+
+struct value *
+value_from_ulongest (struct type *type, ULONGEST num)
+{
+  struct value *val = allocate_value (type);
+
+  pack_unsigned_long (value_contents_raw (val), type, num);
+
+  return val;
+}
+
+
 /* Create a value representing a pointer of type TYPE to the address
    ADDR.  */
 struct value *
diff --git a/gdb/value.h b/gdb/value.h
index 57b4dd7..f64579b 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -342,6 +342,7 @@ extern LONGEST unpack_field_as_long (struct type *type,
 extern void pack_long (gdb_byte *buf, struct type *type, LONGEST num);
 
 extern struct value *value_from_longest (struct type *type, LONGEST num);
+extern struct value *value_from_ulongest (struct type *type, ULONGEST num);
 extern struct value *value_from_pointer (struct type *type, CORE_ADDR addr);
 extern struct value *value_from_double (struct type *type, DOUBLEST num);
 extern struct value *value_from_decfloat (struct type *type,

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [python][patch] Inferior and Thread information support.
  2010-05-24 13:36 [python][patch] Inferior and Thread information support Phil Muldoon
@ 2010-05-24 18:06 ` Eli Zaretskii
  2010-06-10 18:40 ` Tom Tromey
  1 sibling, 0 replies; 21+ messages in thread
From: Eli Zaretskii @ 2010-05-24 18:06 UTC (permalink / raw)
  To: Phil Muldoon; +Cc: gdb-patches

> Date: Mon, 24 May 2010 13:18:10 +0100
> From: Phil Muldoon <pmuldoon@redhat.com>
> 
> This patch adds Python support to inferiors and threads.

Thanks.  A few comments about the documentation parts:

> +@node Inferiors In Python
> +@subsubsection Inferiors In Python

It is usually a good idea to have a @cindex entry at the beginning of
a section whose name is identical to the section name (except all
lower-case).

> +Programs which are being run under @value{GDBN} are called inferiors
> +(@pxref{Inferiors and Programs}).  Python scripts can access
> +information about and manipulate inferiors controlled by @value{GDBN}
> +via objects of the @code{gdb.Inferior} class.

This should probable have a @findex entry for gdb.Inferior.

> +@defmethod Inferior threads
> +This method returns a tuple holding all the threads which are valid
> +when it is called.  If there are no valid threads, the method will
> +return an empty list.

Are "list" and "tuple" synonyms in Python?  If not, perhaps this needs
a slight rewording.

> +@defmethod Inferior read_memory @var{address} @var{length}

I don't think you need @var around the arguments of this method (or
any other methods in the patch).  You only need @var if an argument
includes characters treated specially by Texinfo.

> +Write the contents of @var{buffer} (a Python object which supports the
> +buffer protocol, i.e., a string, an array or the object returned from
> +@code{gdb.read_memory}) to the inferior, starting at @var{address}.

I wonder whether a buffer object warrants a separate short description
which is not in parens in the middle of describing a method.

> +@defmethod Inferior search_memory @var{address} @var{length} @var{pattern} @r{[}@var{size}@r{]} @r{[}@var{max_count}@r{]}
> +Search a region of the inferior memory starting at @var{address} with the
> +given @var{length}.  @var{pattern} can be a string, a byte array, a buffer
> +object, a number, a @code{gdb.Value} object (@pxref{Values From Inferior})
> +or a list or tuple with elements in any combination of those types.

The semantics of tuple or list as PATTERN is not explained here.

>                                                         If @var{size} is
> +zero or not specified, it is taken from the value's type in the current
> +language.

What does this mean if PATTERN is a tuple or a list?

>             This is useful when one wants to specify the search pattern as
> +a mixture of types.

Is "mixture of types" intended to cover the list/tuple case?  If so, I
suggest to use identical words, to minimize the chance of confusing
the reader with terminology that is never explained.

> +@node Threads In Python
> +@subsubsection Threads In Python

See the comment above regarding a @cindex entry here.

> +Python scripts can access information about, and manipulate inferior threads
> +controlled by @value{GDBN}, via objects of the @code{gdb.InferiorThread} class.

And @findex here regarding gdb.InferiorThread.

> +@defivar InferiorThread num
> +ID of the thread, as assigned by GDB.
                                    ^^^
@value{GDBN}.

Okay with those changes.

In addition, I think we need a NEWS entry for these features.

Thanks.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [python][patch] Inferior and Thread information support.
  2010-05-24 13:36 [python][patch] Inferior and Thread information support Phil Muldoon
  2010-05-24 18:06 ` Eli Zaretskii
@ 2010-06-10 18:40 ` Tom Tromey
  2010-06-14 12:42   ` Phil Muldoon
  2010-06-18  6:49   ` Phil Muldoon
  1 sibling, 2 replies; 21+ messages in thread
From: Tom Tromey @ 2010-06-10 18:40 UTC (permalink / raw)
  To: Phil Muldoon; +Cc: gdb-patches ml

>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:

Phil> This patch adds Python support to inferiors and threads.

Sorry for the delay in this review.

Phil>  /* Copied from bfd_put_bits.  */
Phil> -static void
Phil> +void
Phil>  put_bits (bfd_uint64_t data, char *buf, int bits, bfd_boolean big_p)

I don't think you need this change.  Use store_unsigned_integer instead.

I don't understand why this function exists at all.

Phil> +void
Phil> +allocate_pattern_buffer (char **pattern_buf, char **pattern_buf_end,
Phil> +			 ULONGEST *pattern_buf_size)

Phil> +void
Phil> +increase_pattern_buffer (char **pattern_buf, char **pattern_buf_end,
Phil> +			 ULONGEST *pattern_buf_size, int val_bytes)

I think this stuff can be easily done with obstacks.  I prefer we not
add more growable types in situations where we can reuse the ones we
already have.

Phil> +  /* While creating new inferior no inferior thread is available.
Phil> +     Therefore get_current_arch has no valid current frame (and it
Phil> +     would crash).  */
Phil> +  cleanup = ensure_python_env (target_gdbarch, current_language);

You should use python_gdbarch and python_language here.
This occurs a couple of times.

Phil> +/* An observer callback function that is called when an inferior has
Phil> +   been deleted.  Removes the corresponding Python object from the
Phil> +   inferior list, and removes the list's reference to the object.  */
Phil> +static void
Phil> +delete_inferior_object (struct inferior *inf)
Phil> +{
Phil> +  struct cleanup *cleanup;
Phil> +  struct inflist_entry **inf_entry, *inf_tmp;
Phil> +  struct threadlist_entry *th_entry, *th_tmp;
Phil> +
Phil> +  /* Find inferior_object for the given PID.  */
Phil> +  for (inf_entry = &gdbpy_inferior_list; *inf_entry != NULL;
Phil> +       inf_entry = &(*inf_entry)->next)
Phil> +    if ((*inf_entry)->inf_obj->inferior->pid == inf->pid)
Phil> +      break;

It seems strange to compare the pid fields when we could just compare
the inferior objects themselves.

Phil> +/* Implementation of Inferior.frames () -> (gdb.Frame, ...).
Phil> +   Returns a tuple of all frame objects.  */
Phil> +PyObject *
Phil> +thpy_frames (PyObject *self, PyObject *args)
[...]

Phil> +      for (frame = get_current_frame (); frame;
Phil> +	   frame = get_prev_frame (frame))
Phil> +	{
Phil> +	  frame_obj = frame_info_to_frame_object (frame);
Phil> +	  if (frame_obj == NULL)
Phil> +	    {

I don't think this is wise.  It is not uncommon for a crash to cause a
thread to have thousands of frames.

Hm, maybe there is no way to return a frame-in-a-thread and then be able
to iterate.  IOW, a gdb internals limitation.  That is unfortunate (if
true) but I don't think it is a reason for us to go with this API.

One short-term solution would be to get rid of this method.

Phil> +/* Implementation of InferiorThread.newest_frame () -> gdb.Frame.
Phil> +   Returns the newest frame object.  */
Phil> +PyObject *
Phil> +thpy_newest_frame (PyObject *self, PyObject *args)
Phil> +{
Phil> +  struct frame_info *frame;
Phil> +  PyObject *frame_obj = NULL;   /* Initialize to appease gcc warning.  */
Phil> +  thread_object *thread_obj = (thread_object *) self;
Phil> +  struct cleanup *cleanup;
Phil> +  volatile struct gdb_exception except;
Phil> +
Phil> +  THPY_REQUIRE_VALID (thread_obj);
Phil> +
Phil> +  cleanup = make_cleanup_restore_current_thread ();
Phil> +
Phil> +  TRY_CATCH (except, RETURN_MASK_ALL)
Phil> +    {
Phil> +      switch_to_thread (thread_obj->thread->ptid);
Phil> +
Phil> +      frame = get_current_frame ();
Phil> +      frame_obj = frame_info_to_frame_object (frame);
Phil> +    }
Phil> +  GDB_PY_HANDLE_EXCEPTION (except);

I am really not sure about this.

Doesn't switch_to_thread reset the frame cache?
Meaning that the returned frame_obj will immediately be invalid?

You would have to try this with a multi-threaded program, where you are
stopped in thread A but then request a frame in thread B.

Phil> +void
Phil> +gdbpy_initialize_thread (void)
Phil> +{
Phil> +
Phil> +  if (PyType_Ready (&thread_object_type) < 0)

Spurious blank line.

Tom

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [python][patch] Inferior and Thread information support.
  2010-06-10 18:40 ` Tom Tromey
@ 2010-06-14 12:42   ` Phil Muldoon
  2010-06-15 15:24     ` Pedro Alves
  2010-06-15 18:11     ` Tom Tromey
  2010-06-18  6:49   ` Phil Muldoon
  1 sibling, 2 replies; 21+ messages in thread
From: Phil Muldoon @ 2010-06-14 12:42 UTC (permalink / raw)
  To: tromey; +Cc: gdb-patches ml

On 06/10/2010 07:40 PM, Tom Tromey wrote:
>>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:
> 
> Phil> This patch adds Python support to inferiors and threads.
> 
> Sorry for the delay in this review.
> 
> Phil>  /* Copied from bfd_put_bits.  */
> Phil> -static void
> Phil> +void
> Phil>  put_bits (bfd_uint64_t data, char *buf, int bits, bfd_boolean big_p)
> 
> I don't think you need this change.  Use store_unsigned_integer instead.
> 
> I don't understand why this function exists at all.

Me either ...

> 
> Phil> +void
> Phil> +allocate_pattern_buffer (char **pattern_buf, char **pattern_buf_end,
> Phil> +			 ULONGEST *pattern_buf_size)
> 
> Phil> +void
> Phil> +increase_pattern_buffer (char **pattern_buf, char **pattern_buf_end,
> Phil> +			 ULONGEST *pattern_buf_size, int val_bytes)
> 
> I think this stuff can be easily done with obstacks.  I prefer we not
> add more growable types in situations where we can reuse the ones we
> already have.

I've no complaint to using obstacks.  This function basically
wraps/tidies the existing code that was just coded directly in a loop
in parse_find_args.  That code just realloc'd by a factor of two whenever
the buffer was too small.  This code is exactly the same, except it
has been squirrelled away in a function.  So we are not introducing or
adding any more growable types in this patch, just moving the code
bits that already existed into function.  I'm not adverse to changing
that code to use obstacks, that being said!

> 
> Phil> +  /* While creating new inferior no inferior thread is available.
> Phil> +     Therefore get_current_arch has no valid current frame (and it
> Phil> +     would crash).  */
> Phil> +  cleanup = ensure_python_env (target_gdbarch, current_language);
> 
> You should use python_gdbarch and python_language here.
> This occurs a couple of times.

Ok, missed this!


> 
> Phil> +/* An observer callback function that is called when an inferior has
> Phil> +   been deleted.  Removes the corresponding Python object from the
> Phil> +   inferior list, and removes the list's reference to the object.  */
> Phil> +static void
> Phil> +delete_inferior_object (struct inferior *inf)
> Phil> +{
> Phil> +  struct cleanup *cleanup;
> Phil> +  struct inflist_entry **inf_entry, *inf_tmp;
> Phil> +  struct threadlist_entry *th_entry, *th_tmp;
> Phil> +
> Phil> +  /* Find inferior_object for the given PID.  */
> Phil> +  for (inf_entry = &gdbpy_inferior_list; *inf_entry != NULL;
> Phil> +       inf_entry = &(*inf_entry)->next)
> Phil> +    if ((*inf_entry)->inf_obj->inferior->pid == inf->pid)
> Phil> +      break;
> 
> It seems strange to compare the pid fields when we could just compare
> the inferior objects themselves.


Do you mean using the Python object's cmp inbuilt method here?


> 
> Phil> +/* Implementation of Inferior.frames () -> (gdb.Frame, ...).
> Phil> +   Returns a tuple of all frame objects.  */
> Phil> +PyObject *
> Phil> +thpy_frames (PyObject *self, PyObject *args)
> [...]
> 
> Phil> +      for (frame = get_current_frame (); frame;
> Phil> +	   frame = get_prev_frame (frame))
> Phil> +	{
> Phil> +	  frame_obj = frame_info_to_frame_object (frame);
> Phil> +	  if (frame_obj == NULL)
> Phil> +	    {
> 
> I don't think this is wise.  It is not uncommon for a crash to cause a
> thread to have thousands of frames.
> 
> Hm, maybe there is no way to return a frame-in-a-thread and then be able
> to iterate.  IOW, a gdb internals limitation.  That is unfortunate (if
> true) but I don't think it is a reason for us to go with this API.
> 
> One short-term solution would be to get rid of this method.

Ok.

> 
> Phil> +/* Implementation of InferiorThread.newest_frame () -> gdb.Frame.
> Phil> +   Returns the newest frame object.  */
> Phil> +PyObject *
> Phil> +thpy_newest_frame (PyObject *self, PyObject *args)
> Phil> +{
> Phil> +  struct frame_info *frame;
> Phil> +  PyObject *frame_obj = NULL;   /* Initialize to appease gcc warning.  */
> Phil> +  thread_object *thread_obj = (thread_object *) self;
> Phil> +  struct cleanup *cleanup;
> Phil> +  volatile struct gdb_exception except;
> Phil> +
> Phil> +  THPY_REQUIRE_VALID (thread_obj);
> Phil> +
> Phil> +  cleanup = make_cleanup_restore_current_thread ();
> Phil> +
> Phil> +  TRY_CATCH (except, RETURN_MASK_ALL)
> Phil> +    {
> Phil> +      switch_to_thread (thread_obj->thread->ptid);
> Phil> +
> Phil> +      frame = get_current_frame ();
> Phil> +      frame_obj = frame_info_to_frame_object (frame);
> Phil> +    }
> Phil> +  GDB_PY_HANDLE_EXCEPTION (except);
> 
> I am really not sure about this.
> 
> Doesn't switch_to_thread reset the frame cache?
> Meaning that the returned frame_obj will immediately be invalid?
> 
> You would have to try this with a multi-threaded program, where you are
> stopped in thread A but then request a frame in thread B.

I'll investigate this further. I had concerns about this (I think we
chatted a little about this on irc way back when).  I'll write an
inferior test and see.  Maybe Pedro or someone else in that area knows
a little more.


Cheers

Phil


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [python][patch] Inferior and Thread information support.
  2010-06-14 12:42   ` Phil Muldoon
@ 2010-06-15 15:24     ` Pedro Alves
  2010-06-15 18:11     ` Tom Tromey
  1 sibling, 0 replies; 21+ messages in thread
From: Pedro Alves @ 2010-06-15 15:24 UTC (permalink / raw)
  To: gdb-patches; +Cc: Phil Muldoon, tromey

On Monday 14 June 2010 13:42:41, Phil Muldoon wrote:
> On 06/10/2010 07:40 PM, Tom Tromey wrote:
> >>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:
> > 
> > Phil> +  THPY_REQUIRE_VALID (thread_obj);
> > Phil> +
> > Phil> +  cleanup = make_cleanup_restore_current_thread ();
> > Phil> +
> > Phil> +  TRY_CATCH (except, RETURN_MASK_ALL)
> > Phil> +    {
> > Phil> +      switch_to_thread (thread_obj->thread->ptid);
> > Phil> +
> > Phil> +      frame = get_current_frame ();
> > Phil> +      frame_obj = frame_info_to_frame_object (frame);
> > Phil> +    }
> > Phil> +  GDB_PY_HANDLE_EXCEPTION (except);
> > 
> > I am really not sure about this.
> > 
> > Doesn't switch_to_thread reset the frame cache?
> > Meaning that the returned frame_obj will immediately be invalid?
> > 
> > You would have to try this with a multi-threaded program, where you are
> > stopped in thread A but then request a frame in thread B.
> 
> I'll investigate this further. I had concerns about this (I think we
> chatted a little about this on irc way back when).  I'll write an
> inferior test and see.  Maybe Pedro or someone else in that area knows
> a little more.

Tom is correct.  You'll not see a problem with single-thread apps
because switch_to_thread does an early return before invalidating
the frame cache when the thread you're switching to is already current.

In short, don't ever store a frame_info pointer like this.  Instead,
if you need to record the frame somewhere, store a frame_id.
(I haven't really looked at the patch and the use case, just
commenting from the what's quoted above)

-- 
Pedro Alves

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [python][patch] Inferior and Thread information support.
  2010-06-14 12:42   ` Phil Muldoon
  2010-06-15 15:24     ` Pedro Alves
@ 2010-06-15 18:11     ` Tom Tromey
  2010-06-15 18:24       ` Pedro Alves
  2010-06-15 19:58       ` Phil Muldoon
  1 sibling, 2 replies; 21+ messages in thread
From: Tom Tromey @ 2010-06-15 18:11 UTC (permalink / raw)
  To: Phil Muldoon; +Cc: gdb-patches ml

>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:

Phil> I've no complaint to using obstacks.  This function basically
Phil> wraps/tidies the existing code that was just coded directly in a loop
Phil> in parse_find_args.  That code just realloc'd by a factor of two whenever
Phil> the buffer was too small.  This code is exactly the same, except it
Phil> has been squirrelled away in a function.  So we are not introducing or
Phil> adding any more growable types in this patch, just moving the code
Phil> bits that already existed into function.  I'm not adverse to changing
Phil> that code to use obstacks, that being said!

Just for the record -- ordinarily I try not to request cleanups to
existing code as part of a new patch.  It is nice to get cleanups, and
if you want to do them (or if there is a reason for them beyond mere
tidiness) then that is great.  But feel free to push back if I've
erroneously reviewed the context and not the patch.

Phil> +  /* Find inferior_object for the given PID.  */
Phil> +  for (inf_entry = &gdbpy_inferior_list; *inf_entry != NULL;
Phil> +       inf_entry = &(*inf_entry)->next)
Phil> +    if ((*inf_entry)->inf_obj->inferior->pid == inf->pid)
Phil> +      break;
> 
Tom> It seems strange to compare the pid fields when we could just compare
Tom> the inferior objects themselves.

Phil> Do you mean using the Python object's cmp inbuilt method here?

No, I'm just curious why that can't be more simply written:

  if ((*inf_entry)->inf_obj == inf)

Tom

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [python][patch] Inferior and Thread information support.
  2010-06-15 18:11     ` Tom Tromey
@ 2010-06-15 18:24       ` Pedro Alves
  2010-06-15 19:58       ` Phil Muldoon
  1 sibling, 0 replies; 21+ messages in thread
From: Pedro Alves @ 2010-06-15 18:24 UTC (permalink / raw)
  To: gdb-patches, tromey; +Cc: Phil Muldoon

On Tuesday 15 June 2010 19:11:41, Tom Tromey wrote:
> Phil> +  /* Find inferior_object for the given PID.  */
> Phil> +  for (inf_entry = &gdbpy_inferior_list; *inf_entry != NULL;
> Phil> +       inf_entry = &(*inf_entry)->next)
> Phil> +    if ((*inf_entry)->inf_obj->inferior->pid == inf->pid)
> Phil> +      break;
> > 
> Tom> It seems strange to compare the pid fields when we could just compare
> Tom> the inferior objects themselves.
> 
> Phil> Do you mean using the Python object's cmp inbuilt method here?
> 
> No, I'm just curious why that can't be more simply written:
> 
>   if ((*inf_entry)->inf_obj == inf)
> 

In fact, you can have two distinct inferiors both
with pid == 0:

$ gdb
(gdb) add-inferior
(gdb) info inferiors 
  Num  Description       Executable        
  2    <null>                              
* 1    <null> 

-- 
Pedro Alves

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [python][patch] Inferior and Thread information support.
  2010-06-15 18:11     ` Tom Tromey
  2010-06-15 18:24       ` Pedro Alves
@ 2010-06-15 19:58       ` Phil Muldoon
  2010-06-15 20:36         ` Pedro Alves
  1 sibling, 1 reply; 21+ messages in thread
From: Phil Muldoon @ 2010-06-15 19:58 UTC (permalink / raw)
  To: tromey; +Cc: gdb-patches ml

On 06/15/2010 07:11 PM, Tom Tromey wrote:
>>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:
> 
> Phil> I've no complaint to using obstacks.  This function basically
> Phil> wraps/tidies the existing code that was just coded directly in a loop
> Phil> in parse_find_args.  That code just realloc'd by a factor of two whenever
> Phil> the buffer was too small.  This code is exactly the same, except it
> Phil> has been squirrelled away in a function.  So we are not introducing or
> Phil> adding any more growable types in this patch, just moving the code
> Phil> bits that already existed into function.  I'm not adverse to changing
> Phil> that code to use obstacks, that being said!
> 
> Just for the record -- ordinarily I try not to request cleanups to
> existing code as part of a new patch.  It is nice to get cleanups, and
> if you want to do them (or if there is a reason for them beyond mere
> tidiness) then that is great.  But feel free to push back if I've
> erroneously reviewed the context and not the patch.


We talked a little about this on irc.  I think your original instincts
were totally right.  I had second thoughts about it.  We're going through this
bit of code, so lets just fix it -- and I'm happy to do it.  I did a
basic conversion to obstacks today; I'll refine it for the patch
later.  I don't see it being a huge issue. 

> 
> Phil> +  /* Find inferior_object for the given PID.  */
> Phil> +  for (inf_entry = &gdbpy_inferior_list; *inf_entry != NULL;
> Phil> +       inf_entry = &(*inf_entry)->next)
> Phil> +    if ((*inf_entry)->inf_obj->inferior->pid == inf->pid)
> Phil> +      break;
>>
> Tom> It seems strange to compare the pid fields when we could just compare
> Tom> the inferior objects themselves.
> 
> Phil> Do you mean using the Python object's cmp inbuilt method here?
> 
> No, I'm just curious why that can't be more simply written:
> 
>   if ((*inf_entry)->inf_obj == inf)


When I ported this code from Archer I thought the (t)pid equality was
fine.  But as in any porting effort you take the sum of several
authors and decide to rewrite/port what you have.  I thought that this
equality was ok.  OTOH I don't see why either (on your original
question).  I'll investigate further and report back.

Cheers,

Phil

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [python][patch] Inferior and Thread information support.
  2010-06-15 19:58       ` Phil Muldoon
@ 2010-06-15 20:36         ` Pedro Alves
  0 siblings, 0 replies; 21+ messages in thread
From: Pedro Alves @ 2010-06-15 20:36 UTC (permalink / raw)
  To: gdb-patches; +Cc: Phil Muldoon, tromey

On Tuesday 15 June 2010 20:58:15, Phil Muldoon wrote:
> When I ported this code from Archer I thought the (t)pid equality was
> fine.  But as in any porting effort you take the sum of several
> authors and decide to rewrite/port what you have.  I thought that this
> equality was ok.  OTOH I don't see why either (on your original
> question).  I'll investigate further and report back.

Probably when the original code was written, gdb mainline
didn't have the concept of proto inferiors with pid == 0.  That
only came in with the multi-exec work.  Just guessing.

-- 
Pedro Alves

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [python][patch] Inferior and Thread information support.
  2010-06-10 18:40 ` Tom Tromey
  2010-06-14 12:42   ` Phil Muldoon
@ 2010-06-18  6:49   ` Phil Muldoon
  2010-06-18 14:21     ` Doug Evans
  2010-06-18 18:04     ` Tom Tromey
  1 sibling, 2 replies; 21+ messages in thread
From: Phil Muldoon @ 2010-06-18  6:49 UTC (permalink / raw)
  To: tromey; +Cc: gdb-patches ml

On 06/10/2010 07:40 PM, Tom Tromey wrote:

> Phil>  /* Copied from bfd_put_bits.  */
> Phil> -static void
> Phil> +void
> Phil>  put_bits (bfd_uint64_t data, char *buf, int bits, bfd_boolean big_p)
> 
> I don't think you need this change.  Use store_unsigned_integer instead.


I've re-factored the code to not need it.

 
> Phil> +void
> Phil> +allocate_pattern_buffer (char **pattern_buf, char **pattern_buf_end,
> Phil> +			 ULONGEST *pattern_buf_size)
> 
> Phil> +void
> Phil> +increase_pattern_buffer (char **pattern_buf, char **pattern_buf_end,
> Phil> +			 ULONGEST *pattern_buf_size, int val_bytes)
> 
> I think this stuff can be easily done with obstacks.  I prefer we not
> add more growable types in situations where we can reuse the ones we
> already have.


These functions are removed, and we now use obstacks within the Python
code to grow the pattern until it is finalised.


> Phil> +  cleanup = ensure_python_env (target_gdbarch, current_language);
> 
> You should use python_gdbarch and python_language here.
> This occurs a couple of times.


Fixed, thanks.


> Phil> +    if ((*inf_entry)->inf_obj->inferior->pid == inf->pid)
> Phil> +      break;
> 
> It seems strange to compare the pid fields when we could just compare
> the inferior objects themselves.


I've changed this to just compare the objects.

 
> 
> Phil> +      for (frame = get_current_frame (); frame;
> Phil> +	   frame = get_prev_frame (frame))
> Phil> +	{
> Phil> +	  frame_obj = frame_info_to_frame_object (frame);
> Phil> +	  if (frame_obj == NULL)
> Phil> +	    {
> 
> I don't think this is wise.  It is not uncommon for a crash to cause a
> thread to have thousands of frames.

> One short-term solution would be to get rid of this method.


I've removed the API for this.


> Phil> +PyObject *
> Phil> +thpy_newest_frame (PyObject *self, PyObject *args)
> 
> I am really not sure about this.
> 
> Doesn't switch_to_thread reset the frame cache?

This was talked about at length on irc, and on the mailing list too.
As we just store the frame id I think we figured out we were ok.

Latest patch attached.

Cheers,

Phil

--

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 5cb02d8..6dbb284 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -275,6 +275,8 @@ SUBDIR_PYTHON_OBS = \
 	py-cmd.o \
 	py-frame.o \
 	py-function.o \
+	py-inferior.o \
+	py-infthread.o \
 	py-lazy-string.o \
 	py-objfile.o \
 	py-param.o \
@@ -293,6 +295,8 @@ SUBDIR_PYTHON_SRCS = \
 	python/py-cmd.c \
 	python/py-frame.c \
 	python/py-function.c \
+	python/py-inferior.c \
+	python/py-infthread.c \
 	python/py-lazy-string.c \
 	python/py-objfile.c \
 	python/py-param.c \
@@ -2018,6 +2022,14 @@ py-function.o: $(srcdir)/python/py-function.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-function.c
 	$(POSTCOMPILE)
 
+py-inferior.o: $(srcdir)/python/py-inferior.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-inferior.c
+	$(POSTCOMPILE)
+
+py-infthread.o: $(srcdir)/python/py-infthread.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-infthread.c
+	$(POSTCOMPILE)
+
 py-lazy-string.o: $(srcdir)/python/py-lazy-string.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-lazy-string.c
 	$(POSTCOMPILE)
diff --git a/gdb/NEWS b/gdb/NEWS
index af65274..d28bae4 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -100,9 +100,9 @@ is now deprecated.
 * Python scripting
 
 ** The GDB Python API now has access to breakpoints, symbols, symbol
-   tables, program spaces, and frame's code blocks.  Additionally, GDB
-   Parameters can now be created from the API, and manipulated via
-   set/show in the CLI.
+   tables, program spaces, inferiors, threads and frame's code blocks.
+   Additionally, GDB Parameters can now be created from the API, and
+   manipulated via set/show in the CLI.
 
 ** New functions gdb.target_charset, gdb.target_wide_charset,
    gdb.progspaces, gdb.current_progspace, and gdb.string_to_argv.
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 1daa211..d95951f 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -20130,6 +20130,8 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
 * Pretty Printing API::         Pretty-printing values.
 * Selecting Pretty-Printers::   How GDB chooses a pretty-printer.
 * Disabling Pretty-Printers::   Disabling broken printers.
+* Inferiors In Python::         Python representation of inferiors (processes)
+* Threads In Python::           Accessing inferior threads from Python.
 * Commands In Python::          Implementing new commands in Python.
 * Parameters In Python::        Adding new @value{GDBN} parameters.
 * Functions In Python::         Writing new convenience functions.
@@ -20946,6 +20948,147 @@ attribute to the registered function or callable object.  If this attribute
 is present and its value is @code{False}, the printer is disabled, otherwise
 the printer is enabled.
 
+@node Inferiors In Python
+@subsubsection Inferiors In Python
+@cindex inferiors in python
+
+@findex gdb.Inferior
+Programs which are being run under @value{GDBN} are called inferiors
+(@pxref{Inferiors and Programs}).  Python scripts can access
+information about and manipulate inferiors controlled by @value{GDBN}
+via objects of the @code{gdb.Inferior} class.
+
+The following inferior-related functions are available in the @code{gdb}
+module:
+
+@defun inferiors
+Return a tuple containing all inferior objects.
+@end defun
+
+A @code{gdb.Inferior} object has the following attributes:
+
+@table @code
+@defivar Inferior num
+ID of inferior, as assigned by GDB.
+@end defivar
+
+@defivar Inferior pid
+Process ID of the inferior, as assigned by the underlying operating
+system.
+@end defivar
+
+@defivar Inferior was_attached
+Boolean signaling whether the inferior was created using `attach', or
+started by @value{GDBN} itself.
+@end defivar
+@end table
+
+A @code{gdb.Inferior} object has the following methods:
+
+@table @code
+@defmethod Inferior threads
+This method returns a tuple holding all the threads which are valid
+when it is called.  If there are no valid threads, the method will
+return an empty tuple.
+@end defmethod
+
+@findex gdb.read_memory
+@defmethod Inferior read_memory address length
+Read @var{length} bytes of memory from the inferior, starting at
+@var{address}.  Returns a buffer object, which behaves much like an array
+or a string.  It can be modified and given to the @code{gdb.write_memory}
+function.
+@end defmethod
+
+@findex gdb.write_memory
+@defmethod Inferior write_memory address buffer @r{[}length@r{]}
+Write the contents of @var{buffer} to the inferior, starting at
+@var{address}.  The @var{buffer} parameter must be a Python object
+which supports the buffer protocol, i.e., a string, an array or the
+object returned from @code{gdb.read_memory}.  If given, @var{length}
+determines the number of bytes from @var{buffer} to be written.
+@end defmethod
+
+@findex gdb.search_memory
+@defmethod Inferior search_memory address length pattern @r{[}size@r{]} @r{[}max_count@r{]}
+Search a region of the inferior memory starting at @var{address} with the
+given @var{length}.  @var{pattern} can be a string, a byte array, a buffer
+object, a number, a @code{gdb.Value} object (@pxref{Values From
+Inferior}), or a Python list or a tuple with elements in any combination
+of those types.  If @var{pattern} is a list or a tuple, each element
+will be extracted and concatenated together to form @var{pattern}.
+
+If @var{size} is given and is non-zero, it specifies the size in bytes
+of a Python scalar or @code{gdb.Value} in the search pattern.  If
+@var{size} is zero or not specified, the size is taken from the value's type
+in the current language.  If @var{pattern} is a tuple or list, these
+actions apply to each element in the tuple or list.  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.  @var{max_count} is the highest
+number of matches to search for.
+@end defmethod
+@end table
+
+@node Threads In Python
+@subsubsection Threads In Python
+@cindex threads in python
+
+@findex gdb.InferiorThread
+Python scripts can access information about, and manipulate inferior threads
+controlled by @value{GDBN}, via objects of the @code{gdb.InferiorThread} class.
+
+The following thread-related functions are available in the @code{gdb}
+module:
+
+@findex gdb.selected_thread
+@defun selected_thread
+This function returns the thread object for the selected thread.  If there
+is no selected thread, this will return @code{None}.
+@end defun
+
+A @code{gdb.InferiorThread} object has the following attributes:
+
+@table @code
+@defivar InferiorThread num
+ID of the thread, as assigned by GDB.
+@end defivar
+
+@defivar InferiorThread ptid
+ID of the thread, as assigned by the operating system.  This attribute is a
+tuple containing three integers.  The first is the Process ID (PID); the second
+is the Lightweight Process ID (TID), and the third is the Thread ID (TID).
+Either the TID or TID may be 0, which indicates that the operating system
+does not  use that identifier.
+@end defivar
+@end table
+
+A @code{gdb.InferiorThread} object has the following methods:
+
+@table @code
+@defmethod InferiorThread newest_frame
+Return the newest frame thread's stack.
+@end defmethod
+
+@defmethod InferiorThread switch
+This changes @value{GDBN}'s currently selected thread to the one represented
+by this object.
+@end defmethod
+
+@defmethod InferiorThread is_stopped
+Return a Boolean indicating whether the thread is stopped.
+@end defmethod
+
+@defmethod InferiorThread is_running
+Return a Boolean indicating whether the thread is running.
+@end defmethod
+
+@defmethod InferiorThread is_exited
+Return a Boolean indicating whether the thread is exited.
+@end defmethod
+@end table
+
 @node Commands In Python
 @subsubsection Commands In Python
 
diff --git a/gdb/findcmd.c b/gdb/findcmd.c
index ac63a9e..df83805 100644
--- a/gdb/findcmd.c
+++ b/gdb/findcmd.c
@@ -240,6 +240,45 @@ parse_find_args (char *args, ULONGEST *max_countp,
   discard_cleanups (old_cleanups);
 }
 
+/* Drives target_search_memory to sweep through the specified search space,
+   possibly in several iterations (with one call to this function for each
+   iteration).  *START_ADDR is the address where the search starts, and is
+   updated to the next starting address to continue the search.
+   *SEARCH_SPACE_LEN is the amount of bytes which will be searched, and is
+   updated for the next iteration.  PATTERN_BUF holds the pattern to
+   be searched  for, PATTERN_LEN is the size of the pattern in bytes.
+   If a match is found, it's address is put in *FOUND_ADDR.
+
+   Returns 1 if found, 0 if not found, and -1 if there was an error
+   requiring halting of the search (e.g. memory read error).  */
+
+int
+search_memory (CORE_ADDR *start_addr, ULONGEST *search_space_len,
+	       const char *pattern_buf, ULONGEST pattern_len,
+	       CORE_ADDR *found_addr)
+{
+  /* Offset from start of this iteration to the next iteration.  */
+  ULONGEST next_iter_incr;
+  int found;
+
+  found = target_search_memory (*start_addr, *search_space_len,
+				pattern_buf, pattern_len, found_addr);
+  if (found <= 0)
+    return found;
+
+  /* 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;
+
+  return found;
+}
+
 static void
 find_command (char *args, int from_tty)
 {
diff --git a/gdb/python/py-frame.c b/gdb/python/py-frame.c
index 6e9e5d3..a7df810 100644
--- a/gdb/python/py-frame.c
+++ b/gdb/python/py-frame.c
@@ -265,7 +265,7 @@ frapy_function (PyObject *self, PyObject *args)
 /* Convert a frame_info struct to a Python Frame object.
    Sets a Python exception and returns NULL on error.  */
 
-static frame_object *
+PyObject *
 frame_info_to_frame_object (struct frame_info *frame)
 {
   frame_object *frame_obj;
@@ -296,7 +296,7 @@ frame_info_to_frame_object (struct frame_info *frame)
 
   frame_obj->gdbarch = get_frame_arch (frame);
 
-  return frame_obj;
+  return (PyObject *) frame_obj;
 }
 
 /* Implementation of gdb.Frame.older (self) -> gdb.Frame.
@@ -497,7 +497,7 @@ PyObject *
 gdbpy_selected_frame (PyObject *self, PyObject *args)
 {
   struct frame_info *frame;
-  frame_object *frame_obj = NULL;   /* Initialize to appease gcc warning.  */
+  PyObject *frame_obj = NULL;   /* Initialize to appease gcc warning.  */
   volatile struct gdb_exception except;
 
   TRY_CATCH (except, RETURN_MASK_ALL)
@@ -507,7 +507,7 @@ gdbpy_selected_frame (PyObject *self, PyObject *args)
     }
   GDB_PY_HANDLE_EXCEPTION (except);
 
-  return (PyObject *) frame_obj;
+  return frame_obj;
 }
 
 /* Implementation of gdb.stop_reason_string (Integer) -> String.
diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c
new file mode 100644
index 0000000..accea86
--- /dev/null
+++ b/gdb/python/py-inferior.c
@@ -0,0 +1,861 @@
+/* Python interface to inferiors.
+
+   Copyright (C) 2009, 2010 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 "exceptions.h"
+#include "gdbcore.h"
+#include "gdbthread.h"
+#include "inferior.h"
+#include "observer.h"
+#include "python-internal.h"
+#include "arch-utils.h"
+#include "language.h"
+#include "obstack.h"
+#include "gdb_obstack.h"
+
+struct threadlist_entry {
+  thread_object *thread_obj;
+  struct threadlist_entry *next;
+};
+
+typedef struct
+{
+  PyObject_HEAD
+
+  /* The inferior we represent.  */
+  struct inferior *inferior;
+
+  /* thread_object instances under this inferior.  This list owns a
+     reference to each object it contains.  */
+  struct threadlist_entry *threads;
+
+  /* Number of threads in the list.  */
+  int nthreads;
+} inferior_object;
+
+static PyTypeObject inferior_object_type;
+
+typedef struct {
+  PyObject_HEAD
+  void *buffer;
+
+  /* These are kept just for mbpy_str.  */
+  CORE_ADDR addr;
+  CORE_ADDR length;
+} membuf_object;
+
+static PyTypeObject membuf_object_type;
+
+/* Require that INFERIOR be a valid inferior ID.  */
+#define INFPY_REQUIRE_VALID(Inferior)				\
+  do {								\
+    if (!Inferior->inferior)					\
+      {								\
+	PyErr_SetString (PyExc_RuntimeError,			\
+			 _("Inferior no longer exists."));	\
+	return NULL;						\
+      }								\
+  } while (0)
+
+struct inflist_entry {
+  inferior_object *inf_obj;
+  struct inflist_entry *next;
+};
+
+\f
+
+/* Inferior objects list.  */
+
+/* List containing inferior_objects.  This list owns a reference to each
+   object it contains.  */
+static struct inflist_entry *gdbpy_inferior_list;
+
+static int ninferiors;
+
+
+/* An observer callback function that is called when an inferior has
+   been created.  Creates a corresponding Python object for the inferior
+   and adds it to the list.  */
+static void
+add_inferior_object (struct inferior *inf)
+{
+  inferior_object *inf_obj;
+  struct inflist_entry *entry;
+  struct cleanup *cleanup;
+
+  if (!inf)
+    {
+      warning (_("Cannot create Python Inferior object."));
+      return;
+    }
+
+  cleanup = ensure_python_env (python_gdbarch, python_language);
+
+  inf_obj = PyObject_New (inferior_object, &inferior_object_type);
+  if (!inf_obj)
+    {
+      warning (_("Cannot create Python Inferior object."));
+      gdbpy_print_stack ();
+      do_cleanups (cleanup);
+      return;
+    }
+
+  inf_obj->inferior = inf;
+  inf_obj->threads = NULL;
+  inf_obj->nthreads = 0;
+
+  entry = xmalloc (sizeof (struct inflist_entry));
+  entry->inf_obj = inf_obj;
+  entry->next = gdbpy_inferior_list;
+
+  gdbpy_inferior_list = entry;
+
+  ninferiors++;
+
+  do_cleanups (cleanup);
+}
+
+/* An observer callback function that is called when an inferior has
+   been deleted.  Removes the corresponding Python object from the
+   inferior list, and removes the list's reference to the object.  */
+static void
+delete_inferior_object (struct inferior *inf)
+{
+  struct cleanup *cleanup;
+  struct inflist_entry **inf_entry, *inf_tmp;
+  struct threadlist_entry *th_entry, *th_tmp;
+
+  /* Find inferior_object.  */
+  for (inf_entry = &gdbpy_inferior_list; *inf_entry != NULL;
+       inf_entry = &(*inf_entry)->next)
+    if ((*inf_entry)->inf_obj->inferior == inf)
+      break;
+
+  if (!*inf_entry)
+    return;
+
+  cleanup = ensure_python_env (python_gdbarch, python_language);
+
+  inf_tmp = *inf_entry;
+  inf_tmp->inf_obj->inferior = NULL;
+
+  /* Deallocate threads list.  */
+  for (th_entry = inf_tmp->inf_obj->threads; th_entry != NULL;)
+    {
+      Py_DECREF (th_entry->thread_obj);
+
+      th_tmp = th_entry;
+      th_entry = th_entry->next;
+      xfree (th_tmp);
+    }
+
+  inf_tmp->inf_obj->nthreads = 0;
+
+  *inf_entry = (*inf_entry)->next;
+  Py_DECREF (inf_tmp->inf_obj);
+  xfree (inf_tmp);
+
+  ninferiors--;
+
+  do_cleanups (cleanup);
+}
+
+/* Finds the Python Inferior object for the given pid.  Returns a borrowed
+   reference.  */
+PyObject *
+find_inferior_object (int pid)
+{
+  struct inflist_entry *p;
+
+  for (p = gdbpy_inferior_list; p != NULL; p = p->next)
+    if (p->inf_obj->inferior->pid == pid)
+      return (PyObject *) p->inf_obj;
+
+  return NULL;
+}
+
+/* Finds the Python InferiorThread object for the given ptid.  Returns a
+   borrowed reference.  */
+thread_object *
+find_thread_object (ptid_t ptid)
+{
+  int pid;
+  struct inflist_entry *p;
+  struct threadlist_entry *q;
+
+  pid = PIDGET (ptid);
+  for (p = gdbpy_inferior_list; p != NULL; p = p->next)
+    if (p->inf_obj->inferior->pid == pid)
+      for (q = p->inf_obj->threads; q != NULL; q = q->next)
+	if (ptid_equal (q->thread_obj->thread->ptid, ptid))
+	  return q->thread_obj;
+
+  return NULL;
+}
+
+\f
+
+/* Inferior object.  */
+
+static void
+add_thread_object (struct thread_info *tp)
+{
+  struct cleanup *cleanup;
+  thread_object *thread_obj;
+  inferior_object *inf_obj;
+  struct threadlist_entry *entry;
+
+  cleanup = ensure_python_env (python_gdbarch, python_language);
+
+  thread_obj = create_thread_object (tp);
+  if (!thread_obj)
+    {
+      warning (_("Cannot create Python InferiorThread object."));
+      gdbpy_print_stack ();
+      do_cleanups (cleanup);
+      return;
+    }
+
+  inf_obj = (inferior_object *) thread_obj->inf_obj;
+
+  entry = xmalloc (sizeof (struct threadlist_entry));
+  entry->thread_obj = thread_obj;
+  entry->next = inf_obj->threads;
+
+  inf_obj->threads = entry;
+  inf_obj->nthreads++;
+
+  do_cleanups (cleanup);
+}
+
+static void
+delete_thread_object (struct thread_info *tp, int ignore)
+{
+  struct cleanup *cleanup;
+  inferior_object *inf_obj;
+  thread_object *thread_obj;
+  struct threadlist_entry **entry, *tmp;
+
+  inf_obj = (inferior_object *) find_inferior_object (PIDGET(tp->ptid));
+  if (!inf_obj)
+    return;
+
+  /* Find thread entry in its inferior's thread_list.  */
+  for (entry = &inf_obj->threads; *entry != NULL; entry =
+	 &(*entry)->next)
+    if ((*entry)->thread_obj->thread == tp)
+      break;
+
+  if (!*entry)
+    return;
+
+  cleanup = ensure_python_env (python_gdbarch, python_language);
+
+  tmp = *entry;
+  tmp->thread_obj->thread = NULL;
+
+  *entry = (*entry)->next;
+  inf_obj->nthreads--;
+
+  Py_DECREF (tmp->thread_obj);
+  xfree (tmp);
+
+  do_cleanups (cleanup);
+}
+
+static PyObject *
+infpy_threads (PyObject *self, PyObject *args)
+{
+  int i;
+  struct threadlist_entry *entry;
+  inferior_object *inf_obj = (inferior_object *) self;
+  PyObject *tuple;
+
+  INFPY_REQUIRE_VALID (inf_obj);
+
+  tuple = PyTuple_New (inf_obj->nthreads);
+  if (!tuple)
+    return NULL;
+
+  /* The list is in reverse order of thread age (i.e., newest comes
+     first).  */
+  for (i = 0, entry = inf_obj->threads; i < inf_obj->nthreads;
+       i++, entry = entry->next)
+    {
+      Py_INCREF (entry->thread_obj);
+      PyTuple_SET_ITEM (tuple, i, (PyObject *) entry->thread_obj);
+    }
+
+  return tuple;
+}
+
+static PyObject *
+infpy_get_num (PyObject *self, void *closure)
+{
+  inferior_object *inf = (inferior_object *) self;
+
+  INFPY_REQUIRE_VALID (inf);
+
+  return PyLong_FromLong (inf->inferior->num);
+}
+
+static PyObject *
+infpy_get_pid (PyObject *self, void *closure)
+{
+  inferior_object *inf = (inferior_object *) self;
+
+  INFPY_REQUIRE_VALID (inf);
+
+  return PyLong_FromLong (inf->inferior->pid);
+}
+
+static PyObject *
+infpy_get_was_attached (PyObject *self, void *closure)
+{
+  inferior_object *inf = (inferior_object *) self;
+  INFPY_REQUIRE_VALID (inf);
+  if (inf->inferior->attach_flag)
+    Py_RETURN_TRUE;
+  Py_RETURN_FALSE;
+}
+
+\f
+
+/* Implementation of gdb.inferiors () -> (gdb.Inferior, ...).
+   Returns a list of all inferiors.  */
+PyObject *
+gdbpy_inferiors (PyObject *unused, PyObject *unused2)
+{
+  int i;
+  struct inflist_entry *entry;
+  PyObject *tuple;
+
+  tuple = PyTuple_New (ninferiors);
+  if (!tuple)
+    return NULL;
+
+  /* The list is in reverse order of inferior age (i.e., newest comes
+     first).  */
+  for (i = 0, entry = gdbpy_inferior_list;
+       i < ninferiors;
+       i++, entry = entry->next)
+    {
+      Py_INCREF (entry->inf_obj);
+      PyTuple_SET_ITEM (tuple, i, (PyObject *) entry->inf_obj);
+    }
+
+  return tuple;
+}
+
+\f
+
+/* Membuf and memory manipulation.  */
+
+/* Implementation of gdb.read_memory (address, length).
+   Returns a Python buffer object with LENGTH bytes of the inferior's
+   memory at ADDRESS.  Both arguments are integers.  */
+static PyObject *
+infpy_read_memory (PyObject *self, PyObject *args)
+{
+  int error = 0;
+  CORE_ADDR addr, length;
+  void *buffer = NULL;
+  membuf_object *membuf_obj;
+  PyObject *addr_obj, *length_obj;
+  struct cleanup *cleanups;
+  volatile struct gdb_exception except;
+
+  if (! PyArg_ParseTuple (args, "OO", &addr_obj, &length_obj))
+    return NULL;
+
+  cleanups = make_cleanup (null_cleanup, NULL);
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      if (!get_addr_from_python (addr_obj, &addr)
+	  || !get_addr_from_python (length_obj, &length))
+	{
+	  error = 1;
+	  break;
+	}
+
+      buffer = xmalloc (length);
+      make_cleanup (xfree, buffer);
+
+      read_memory (addr, buffer, length);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  if (error)
+    {
+      do_cleanups (cleanups);
+      return NULL;
+    }
+
+  membuf_obj = PyObject_New (membuf_object, &membuf_object_type);
+  if (membuf_obj == NULL)
+    {
+      PyErr_SetString (PyExc_MemoryError,
+		       _("Could not allocate memory buffer object."));
+      do_cleanups (cleanups);
+      return NULL;
+    }
+
+  discard_cleanups (cleanups);
+
+  membuf_obj->buffer = buffer;
+  membuf_obj->addr = addr;
+  membuf_obj->length = length;
+
+  return PyBuffer_FromReadWriteObject ((PyObject *) membuf_obj, 0,
+				       Py_END_OF_BUFFER);
+}
+
+/* Implementation of gdb.write_memory (address, buffer [, length]).
+   Writes the contents of BUFFER (a Python object supporting the read
+   buffer protocol) at ADDRESS in the inferior's memory.  Write LENGTH
+   bytes from BUFFER, or its entire contents if the argument is not
+   provided.  The function returns nothing.  */
+static PyObject *
+infpy_write_memory (PyObject *self, PyObject *args)
+{
+  int buf_len, error = 0;
+  const char *buffer;
+  CORE_ADDR addr, length;
+  PyObject *addr_obj, *length_obj = NULL;
+  volatile struct gdb_exception except;
+
+  if (! PyArg_ParseTuple (args, "Os#|O", &addr_obj, &buffer, &buf_len,
+			  &length_obj))
+    return NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      if (!get_addr_from_python (addr_obj, &addr))
+	{
+	  error = 1;
+	  break;
+	}
+
+      if (!length_obj)
+	length = buf_len;
+      else if (!get_addr_from_python (length_obj, &length))
+	{
+	  error = 1;
+	  break;
+	}
+
+      write_memory (addr, buffer, length);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  if (error)
+    return NULL;
+
+  Py_RETURN_NONE;
+}
+
+/* Destructor of Membuf objects.  */
+static void
+mbpy_dealloc (PyObject *self)
+{
+  xfree (((membuf_object *) self)->buffer);
+  self->ob_type->tp_free (self);
+}
+
+/* Return a description of the Membuf object.  */
+static PyObject *
+mbpy_str (PyObject *self)
+{
+  membuf_object *membuf_obj = (membuf_object *) self;
+
+  return PyString_FromFormat (_("Memory buffer for address %s, \
+which is %s bytes long."),
+			      paddress (python_gdbarch, membuf_obj->addr),
+			      pulongest (membuf_obj->length));
+}
+
+static Py_ssize_t
+get_read_buffer (PyObject *self, Py_ssize_t segment, void **ptrptr)
+{
+  membuf_object *membuf_obj = (membuf_object *) self;
+
+  if (segment)
+    {
+      PyErr_SetString (PyExc_SystemError,
+		       _("The memory buffer supports only one segment."));
+      return -1;
+    }
+
+  *ptrptr = membuf_obj->buffer;
+
+  return membuf_obj->length;
+}
+
+static Py_ssize_t
+get_write_buffer (PyObject *self, Py_ssize_t segment, void **ptrptr)
+{
+  return get_read_buffer (self, segment, ptrptr);
+}
+
+static Py_ssize_t
+get_seg_count (PyObject *self, Py_ssize_t *lenp)
+{
+  if (lenp)
+    *lenp = ((membuf_object *) self)->length;
+
+  return 1;
+}
+
+static Py_ssize_t
+get_char_buffer (PyObject *self, Py_ssize_t segment, char **ptrptr)
+{
+  void *ptr = NULL;
+  Py_ssize_t ret;
+
+  ret = get_read_buffer (self, segment, &ptr);
+  *ptrptr = (char *) ptr;
+
+  return ret;
+}
+
+/* Adds GDB value V to the pattern buffer in *PATTERN_BUF.  If SIZE is
+   not zero, it specifies the number of bytes from V to add to
+   *PATTERN_OBSTACK.  */
+static void
+add_value_pattern (struct value *v, int size,
+		   struct obstack *pattern_obstack)
+{
+  int val_bytes;
+  if (size)
+    val_bytes = size;
+  else
+    val_bytes = TYPE_LENGTH (value_type (v));
+
+  obstack_grow (pattern_obstack, value_contents (v), val_bytes);
+}
+
+/* This function does the actual work of constructing the pattern
+   buffer from OBJ.  If OBJ is an object which implements the read
+   buffer protocol (such as a string, a byte array or gdb.Membuf),
+   then its contents are directly added to PATTERN_OBSTACK.  If it is a
+   list, then this function is recursively called for each of its
+   elements.  If OBJ is an object which can be converted to a GDB
+   value, then the contents of the value are added to PATTERN_OBSTACK.
+   If SIZE is different than zero, then it limits the number of bytes
+   which are copied to the buffer in case OBJ is converted to a GDB
+   value.  That means that SIZE influences only Python scalars and
+   gdb.Value objects.
+
+   Returns 1 on success or 0 on failure, with a Python exception set.
+   This function can also throw GDB exceptions.  */
+static int
+add_pattern_element (PyObject *obj, int size,
+		     struct obstack *pattern_obstack)
+{
+  if (PyObject_CheckReadBuffer (obj))
+    {
+      /* Handle string, Unicode string, byte array, gdb.Membuf and any
+         other object implementing the buffer protocol.  The SIZE
+         parameter is ignored in this case.  */
+      Py_ssize_t val_bytes;
+      const void *buffer;
+
+      if (PyObject_AsReadBuffer (obj, &buffer, &val_bytes) == -1)
+	return 0;
+
+      obstack_grow (pattern_obstack, buffer, val_bytes);
+    }
+  else if (gdbpy_is_value_object (obj))
+    add_value_pattern (value_object_to_value (obj), size, pattern_obstack);
+  else if (PySequence_Check (obj))
+    {
+      /* Handle lists and tuples.  */
+      Py_ssize_t i, num_objs;
+
+      num_objs = PySequence_Size (obj);
+      for (i = 0; i < num_objs; i++)
+	if (!add_pattern_element (PySequence_GetItem (obj, i), size,
+				  pattern_obstack))
+	  return 0;
+    }
+  else
+    {
+      /* See if we can convert from a Python object to a GDB value.  */
+      struct value *v = convert_value_from_python (obj);
+
+      if (v)
+	add_value_pattern (v, size, pattern_obstack);
+      else
+	return 0;
+    }
+
+  return 1;
+}
+
+/* Implementation of
+   gdb.search_memory (address, length, pattern [, size] [, max_count]).
+   The third argument may be either a pattern, or a list or tuple of
+   patterns to be searched.  Size is the size in bytes of each search
+   query value, either 1, 2, 4 or 8.  Returns a list of the addresses
+   where matches were found.  */
+static PyObject *
+infpy_search_memory (PyObject *self, PyObject *args, PyObject *kw)
+{
+  int size = 0;
+  unsigned int found_count = 0;
+  long max_count = 0;
+  CORE_ADDR start_addr, length;
+  static char *keywords[] = { "address", "length", "pattern", "size",
+			      "max_count", NULL };
+  ULONGEST search_space_len;
+  PyObject *pattern, *list = NULL, *start_addr_obj, *length_obj;
+  volatile struct gdb_exception except;
+  struct obstack pattern_obstack;
+  struct cleanup *cleanups;
+
+  if (! PyArg_ParseTupleAndKeywords (args, kw, "OOO|il", keywords,
+				     &start_addr_obj, &length_obj, &pattern,
+				     &size, &max_count))
+    return NULL;
+
+  if (!max_count)
+    max_count = LONG_MAX;
+
+  if (size != 0 && size != 1 && size != 2 && size != 4 && size != 8)
+    {
+      PyErr_SetString (PyExc_ValueError, _("Invalid pattern size."));
+      return NULL;
+    }
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      if (get_addr_from_python (start_addr_obj, &start_addr)
+	  && get_addr_from_python (length_obj, &length))
+	{
+	  if (!length)
+	    {
+	      PyErr_SetString (PyExc_ValueError,
+			       _("Search range is empty."));
+	      break;
+	    }
+	  /* Watch for overflows.  */
+	  else if (length > CORE_ADDR_MAX
+		   || (start_addr + length - 1) < start_addr)
+	    {
+	      PyErr_SetString (PyExc_ValueError,
+			       _("The search range too large."));
+
+	      break;
+	    }
+
+	  search_space_len = length;
+
+	  obstack_init (&pattern_obstack);
+	  cleanups = make_cleanup_obstack_free (&pattern_obstack);
+
+	  if (add_pattern_element (pattern, size, &pattern_obstack))
+	    {
+	      ULONGEST pattern_len = obstack_object_size (&pattern_obstack);
+	      char *pattern_buf = obstack_finish (&pattern_obstack);
+
+	      list = PyList_New (0);
+
+	      while (search_space_len >= pattern_len
+		     && found_count <  max_count)
+		{
+		  CORE_ADDR found_addr;
+		  int found;
+
+		  found = search_memory (&start_addr, &search_space_len,
+					 pattern_buf, pattern_len,
+					 &found_addr);
+		  if (found <= 0)
+		    break;
+
+		  PyList_Append (list, PyLong_FromUnsignedLong (found_addr));
+		  ++found_count;
+		}
+	    }
+	  do_cleanups (cleanups);
+	}
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  return list;
+}
+
+\f
+
+void
+gdbpy_initialize_inferior (void)
+{
+  if (PyType_Ready (&inferior_object_type) < 0)
+    return;
+
+  Py_INCREF (&inferior_object_type);
+  PyModule_AddObject (gdb_module, "Inferior",
+		      (PyObject *) &inferior_object_type);
+
+  gdbpy_inferior_list = NULL;
+  ninferiors = 0;
+
+  observer_attach_inferior_appeared (add_inferior_object);
+  observer_attach_inferior_exit (delete_inferior_object);
+  observer_attach_new_thread (add_thread_object);
+  observer_attach_thread_exit (delete_thread_object);
+
+  if (PyType_Ready (&membuf_object_type) < 0)
+    return;
+
+  Py_INCREF (&membuf_object_type);
+  PyModule_AddObject (gdb_module, "Membuf", (PyObject *) &membuf_object_type);
+}
+
+\f
+
+static PyGetSetDef inferior_object_getset[] =
+{
+  { "num", infpy_get_num, NULL, "ID of inferior, as assigned by GDB.", NULL },
+  { "pid", infpy_get_pid, NULL, "PID of inferior, as assigned by the OS.",
+    NULL },
+  { "was_attached", infpy_get_was_attached, NULL,
+    "True if the inferior was created using 'attach'.", NULL },
+  { NULL }
+};
+
+static PyMethodDef inferior_object_methods[] =
+{
+  { "threads", infpy_threads, METH_NOARGS,
+    "Return all the threads of this inferior." },
+  { "read_memory", infpy_read_memory, METH_VARARGS,
+    "read_memory (address, length) -> buffer\n\
+Return a buffer object for reading from the inferior's memory." },
+  { "write_memory", infpy_write_memory, METH_VARARGS,
+    "write_memory (address, buffer [, length])\n\
+Write the given buffer object to the inferior's memory." },
+  { "search_memory", (PyCFunction) infpy_search_memory,
+    METH_VARARGS | METH_KEYWORDS,
+    "search_memory (address, length, pattern [, size] [, max_count]) -> list\n\
+Return a list with the addresses where matches were found." },
+
+  { NULL }
+};
+
+static PyTypeObject inferior_object_type =
+{
+  PyObject_HEAD_INIT (NULL)
+  0,				  /* ob_size */
+  "gdb.Inferior",		  /* tp_name */
+  sizeof (inferior_object),	  /* tp_basicsize */
+  0,				  /* tp_itemsize */
+  0,				  /* tp_dealloc */
+  0,				  /* tp_print */
+  0,				  /* tp_getattr */
+  0,				  /* tp_setattr */
+  0,				  /* tp_compare */
+  0,				  /* tp_repr */
+  0,				  /* tp_as_number */
+  0,				  /* tp_as_sequence */
+  0,				  /* tp_as_mapping */
+  0,				  /* tp_hash  */
+  0,				  /* tp_call */
+  0,				  /* tp_str */
+  0,				  /* tp_getattro */
+  0,				  /* tp_setattro */
+  0,				  /* tp_as_buffer */
+  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER,  /* tp_flags */
+  "GDB inferior object",	  /* tp_doc */
+  0,				  /* tp_traverse */
+  0,				  /* tp_clear */
+  0,				  /* tp_richcompare */
+  0,				  /* tp_weaklistoffset */
+  0,				  /* tp_iter */
+  0,				  /* tp_iternext */
+  inferior_object_methods,	  /* tp_methods */
+  0,				  /* tp_members */
+  inferior_object_getset,	  /* tp_getset */
+  0,				  /* tp_base */
+  0,				  /* tp_dict */
+  0,				  /* tp_descr_get */
+  0,				  /* tp_descr_set */
+  0,				  /* tp_dictoffset */
+  0,				  /* tp_init */
+  0				  /* tp_alloc */
+};
+
+\f
+
+/* Python doesn't provide a decent way to get compatibility here.  */
+#if HAVE_LIBPYTHON2_4
+#define CHARBUFFERPROC_NAME getcharbufferproc
+#else
+#define CHARBUFFERPROC_NAME charbufferproc
+#endif
+
+static PyBufferProcs buffer_procs = {
+  get_read_buffer,
+  get_write_buffer,
+  get_seg_count,
+  /* The cast here works around a difference between Python 2.4 and
+     Python 2.5.  */
+  (CHARBUFFERPROC_NAME) get_char_buffer
+};
+
+static PyTypeObject membuf_object_type = {
+  PyObject_HEAD_INIT (NULL)
+  0,				  /*ob_size*/
+  "gdb.Membuf",			  /*tp_name*/
+  sizeof (membuf_object),	  /*tp_basicsize*/
+  0,				  /*tp_itemsize*/
+  mbpy_dealloc,			  /*tp_dealloc*/
+  0,				  /*tp_print*/
+  0,				  /*tp_getattr*/
+  0,				  /*tp_setattr*/
+  0,				  /*tp_compare*/
+  0,				  /*tp_repr*/
+  0,				  /*tp_as_number*/
+  0,				  /*tp_as_sequence*/
+  0,				  /*tp_as_mapping*/
+  0,				  /*tp_hash */
+  0,				  /*tp_call*/
+  mbpy_str,			  /*tp_str*/
+  0,				  /*tp_getattro*/
+  0,				  /*tp_setattro*/
+  &buffer_procs,		  /*tp_as_buffer*/
+  Py_TPFLAGS_DEFAULT,		  /*tp_flags*/
+  "GDB memory buffer object", 	  /*tp_doc*/
+  0,				  /* tp_traverse */
+  0,				  /* tp_clear */
+  0,				  /* tp_richcompare */
+  0,				  /* tp_weaklistoffset */
+  0,				  /* tp_iter */
+  0,				  /* tp_iternext */
+  0,				  /* tp_methods */
+  0,				  /* tp_members */
+  0,				  /* tp_getset */
+  0,				  /* tp_base */
+  0,				  /* tp_dict */
+  0,				  /* tp_descr_get */
+  0,				  /* tp_descr_set */
+  0,				  /* tp_dictoffset */
+  0,				  /* tp_init */
+  0,				  /* tp_alloc */
+  PyType_GenericNew		  /* tp_new */
+};
diff --git a/gdb/python/py-infthread.c b/gdb/python/py-infthread.c
new file mode 100644
index 0000000..dfeb285
--- /dev/null
+++ b/gdb/python/py-infthread.c
@@ -0,0 +1,303 @@
+/* Python interface to inferior threads.
+
+   Copyright (C) 2009, 2010 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 "exceptions.h"
+#include "gdbthread.h"
+#include "inferior.h"
+#include "python-internal.h"
+
+static PyTypeObject thread_object_type;
+
+/* Require that INFERIOR be a valid inferior ID.  */
+#define THPY_REQUIRE_VALID(Thread)				\
+  do {								\
+    if (!Thread->thread)					\
+      {								\
+	PyErr_SetString (PyExc_RuntimeError,			\
+			 _("Thread no longer exists."));	\
+	return NULL;						\
+      }								\
+  } while (0)
+
+\f
+
+thread_object *
+create_thread_object (struct thread_info *tp)
+{
+  thread_object *thread_obj;
+
+  thread_obj = PyObject_New (thread_object, &thread_object_type);
+  if (!thread_obj)
+    return NULL;
+
+  thread_obj->thread = tp;
+  thread_obj->inf_obj = find_inferior_object (PIDGET (tp->ptid));
+  Py_INCREF (thread_obj->inf_obj);
+
+  return thread_obj;
+}
+
+\f
+
+static void
+thpy_dealloc (PyObject *self)
+{
+  Py_DECREF (((thread_object *) self)->inf_obj);
+  self->ob_type->tp_free (self);
+}
+
+static PyObject *
+thpy_get_num (PyObject *self, void *closure)
+{
+  thread_object *thread_obj = (thread_object *) self;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  return PyLong_FromLong (thread_obj->thread->num);
+}
+
+/* Getter for InferiorThread.ptid  -> (pid, lwp, tid).
+   Returns a tuple with the thread's ptid components.  */
+static PyObject *
+thpy_get_ptid (PyObject *self, void *closure)
+{
+  int pid;
+  long tid, lwp;
+  thread_object *thread_obj = (thread_object *) self;
+  PyObject *ret;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  ret = PyTuple_New (3);
+  if (!ret)
+    return NULL;
+
+  pid = ptid_get_pid (thread_obj->thread->ptid);
+  lwp = ptid_get_lwp (thread_obj->thread->ptid);
+  tid = ptid_get_tid (thread_obj->thread->ptid);
+
+  PyTuple_SET_ITEM (ret, 0, PyInt_FromLong (pid));
+  PyTuple_SET_ITEM (ret, 1, PyInt_FromLong (lwp));
+  PyTuple_SET_ITEM (ret, 2, PyInt_FromLong (tid));
+
+  return ret;
+}
+
+\f
+
+/* Implementation of InferiorThread.newest_frame () -> gdb.Frame.
+   Returns the newest frame object.  */
+PyObject *
+thpy_newest_frame (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame;
+  PyObject *frame_obj = NULL;   /* Initialize to appease gcc warning.  */
+  thread_object *thread_obj = (thread_object *) self;
+  struct cleanup *cleanup;
+  volatile struct gdb_exception except;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  cleanup = make_cleanup_restore_current_thread ();
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      switch_to_thread (thread_obj->thread->ptid);
+
+      frame = get_current_frame ();
+      frame_obj = frame_info_to_frame_object (frame);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  do_cleanups (cleanup);
+
+  return frame_obj;
+}
+
+/* Implementation of InferiorThread.switch ().
+   Makes this the GDB selected thread.  */
+static PyObject *
+thpy_switch (PyObject *self, PyObject *args)
+{
+  thread_object *thread_obj = (thread_object *) self;
+  struct cleanup *cleanup;
+  volatile struct gdb_exception except;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      switch_to_thread (thread_obj->thread->ptid);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  Py_RETURN_NONE;
+}
+
+/* Implementation of InferiorThread.is_stopped () -> Boolean.
+   Return whether the thread is stopped.  */
+static PyObject *
+thpy_is_stopped (PyObject *self, PyObject *args)
+{
+  thread_object *thread_obj = (thread_object *) self;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  if (is_stopped (thread_obj->thread->ptid))
+    Py_RETURN_TRUE;
+
+  Py_RETURN_FALSE;
+}
+
+/* Implementation of InferiorThread.is_running () -> Boolean.
+   Return whether the thread is running.  */
+static PyObject *
+thpy_is_running (PyObject *self, PyObject *args)
+{
+  thread_object *thread_obj = (thread_object *) self;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  if (is_running (thread_obj->thread->ptid))
+    Py_RETURN_TRUE;
+
+  Py_RETURN_FALSE;
+}
+
+/* Implementation of InferiorThread.is_exited () -> Boolean.
+   Return whether the thread is exited.  */
+static PyObject *
+thpy_is_exited (PyObject *self, PyObject *args)
+{
+  thread_object *thread_obj = (thread_object *) self;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  if (is_exited (thread_obj->thread->ptid))
+    Py_RETURN_TRUE;
+
+  Py_RETURN_FALSE;
+}
+
+\f
+
+/* Implementation of gdb.selected_thread () -> gdb.InferiorThread.
+   Returns the selected thread object.  */
+PyObject *
+gdbpy_selected_thread (PyObject *self, PyObject *args)
+{
+  PyObject *thread_obj;
+
+  thread_obj = (PyObject *) find_thread_object (inferior_ptid);
+  if (thread_obj)
+    {
+      Py_INCREF (thread_obj);
+      return thread_obj;
+    }
+
+  Py_RETURN_NONE;
+}
+
+\f
+
+void
+gdbpy_initialize_thread (void)
+{
+  if (PyType_Ready (&thread_object_type) < 0)
+    return;
+
+  Py_INCREF (&thread_object_type);
+  PyModule_AddObject (gdb_module, "InferiorThread",
+		      (PyObject *) &thread_object_type);
+}
+
+\f
+
+static PyGetSetDef thread_object_getset[] =
+{
+  { "num", thpy_get_num, NULL, "ID of the thread, as assigned by GDB.", NULL },
+  { "ptid", thpy_get_ptid, NULL, "ID of the thread, as assigned by the OS.",
+    NULL },
+
+  { NULL }
+};
+
+static PyMethodDef thread_object_methods[] =
+{
+  { "newest_frame", thpy_newest_frame, METH_NOARGS,
+    "newest_frame () -> gdb.Frame\n\
+Return the newest frame in the thread." },
+  { "switch", thpy_switch, METH_NOARGS,
+    "switch ()\n\
+Makes this the GDB selected thread." },
+  { "is_stopped", thpy_is_stopped, METH_NOARGS,
+    "is_stopped () -> Boolean\n\
+Return whether the thread is stopped." },
+  { "is_running", thpy_is_running, METH_NOARGS,
+    "is_running () -> Boolean\n\
+Return whether the thread is running." },
+  { "is_exited", thpy_is_exited, METH_NOARGS,
+    "is_exited () -> Boolean\n\
+Return whether the thread is exited." },
+
+  { NULL }
+};
+
+static PyTypeObject thread_object_type =
+{
+  PyObject_HEAD_INIT (NULL)
+  0,				  /*ob_size*/
+  "gdb.InferiorThread",		  /*tp_name*/
+  sizeof (thread_object),	  /*tp_basicsize*/
+  0,				  /*tp_itemsize*/
+  thpy_dealloc,			  /*tp_dealloc*/
+  0,				  /*tp_print*/
+  0,				  /*tp_getattr*/
+  0,				  /*tp_setattr*/
+  0,				  /*tp_compare*/
+  0,				  /*tp_repr*/
+  0,				  /*tp_as_number*/
+  0,				  /*tp_as_sequence*/
+  0,				  /*tp_as_mapping*/
+  0,				  /*tp_hash */
+  0,				  /*tp_call*/
+  0,				  /*tp_str*/
+  0,				  /*tp_getattro*/
+  0,				  /*tp_setattro*/
+  0,				  /*tp_as_buffer*/
+  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER,  /*tp_flags*/
+  "GDB thread object",		  /* tp_doc */
+  0,				  /* tp_traverse */
+  0,				  /* tp_clear */
+  0,				  /* tp_richcompare */
+  0,				  /* tp_weaklistoffset */
+  0,				  /* tp_iter */
+  0,				  /* tp_iternext */
+  thread_object_methods,	  /* tp_methods */
+  0,				  /* tp_members */
+  thread_object_getset,		  /* tp_getset */
+  0,				  /* tp_base */
+  0,				  /* tp_dict */
+  0,				  /* tp_descr_get */
+  0,				  /* tp_descr_set */
+  0,				  /* tp_dictoffset */
+  0,				  /* tp_init */
+  0				  /* tp_alloc */
+};
diff --git a/gdb/python/py-utils.c b/gdb/python/py-utils.c
index 944e6b4..90609fa 100644
--- a/gdb/python/py-utils.c
+++ b/gdb/python/py-utils.c
@@ -19,6 +19,7 @@
 
 #include "defs.h"
 #include "charset.h"
+#include "value.h"
 #include "python-internal.h"
 
 
@@ -272,3 +273,51 @@ gdbpy_exception_to_string (PyObject *ptype, PyObject *pvalue)
 
   return str;
 }
+
+/* Converts OBJ to a CORE_ADDR value.
+
+   Returns 1 on success or 0 on failure, with a Python exception set.  This
+   function can also throw GDB exceptions.
+*/
+
+int
+get_addr_from_python (PyObject *obj, CORE_ADDR *addr)
+{
+  if (gdbpy_is_value_object (obj))
+    *addr = value_as_address (value_object_to_value (obj));
+  else if (PyLong_Check (obj))
+    {
+      /* Assume CORE_ADDR corresponds to unsigned long.  */
+      *addr = PyLong_AsUnsignedLong (obj);
+      if (PyErr_Occurred () != NULL)
+	return 0;
+    }
+  else if (PyInt_Check (obj))
+    {
+      long val;
+
+      /* Assume CORE_ADDR corresponds to unsigned long.  */
+      val = PyInt_AsLong (obj);
+
+      if (val >= 0)
+	*addr = val;
+      else
+      {
+	/* If no error ocurred, VAL is indeed negative.  */
+	if (PyErr_Occurred () != NULL)
+	  return 0;
+
+	PyErr_SetString (PyExc_ValueError,
+			 _("Supplied address is negative."));
+	return 0;
+      }
+    }
+  else
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       _("Invalid type for address."));
+      return 0;
+    }
+
+  return 1;
+}
diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c
index eb20de8..2024021 100644
--- a/gdb/python/py-value.c
+++ b/gdb/python/py-value.c
@@ -43,6 +43,10 @@
 /* Python's long type corresponds to C's long long type.  */
 #define builtin_type_pylong builtin_type (python_gdbarch)->builtin_long_long
 
+/* Python's long type corresponds to C's long long type.  Unsigned version.  */
+#define builtin_type_upylong builtin_type \
+  (python_gdbarch)->builtin_unsigned_long_long
+
 #define builtin_type_pybool \
   language_bool_type (python_language, python_gdbarch)
 
@@ -961,7 +965,34 @@ convert_value_from_python (PyObject *obj)
 	{
 	  LONGEST l = PyLong_AsLongLong (obj);
 
-	  if (! PyErr_Occurred ())
+	  if (PyErr_Occurred ())
+	    {
+	      /* If the error was an overflow, we can try converting to
+	         ULONGEST instead.  */
+	      if (PyErr_ExceptionMatches (PyExc_OverflowError))
+		{
+		  PyObject *etype, *evalue, *etraceback, *zero;
+
+		  PyErr_Fetch (&etype, &evalue, &etraceback);
+		  zero = PyInt_FromLong (0);
+
+		  /* Check whether obj is positive.  */
+		  if (PyObject_RichCompareBool (obj, zero, Py_GT) > 0)
+		    {
+		      ULONGEST ul;
+
+		      ul = PyLong_AsUnsignedLongLong (obj);
+		      if (! PyErr_Occurred ())
+			value = value_from_ulongest (builtin_type_upylong, ul);
+		    }
+		  else
+		    /* There's nothing we can do.  */
+		    PyErr_Restore (etype, evalue, etraceback);
+
+		  Py_DECREF (zero);
+		}
+	    }
+	  else
 	    value = value_from_longest (builtin_type_pylong, l);
 	}
       else if (PyFloat_Check (obj))
@@ -1028,6 +1059,14 @@ gdbpy_history (PyObject *self, PyObject *args)
   return value_to_value_object (res_val);
 }
 
+/* Returns 1 in OBJ is a gdb.Value object, 0 otherwise.  */
+
+int
+gdbpy_is_value_object (PyObject *obj)
+{
+  return PyObject_TypeCheck (obj, &value_object_type);
+}
+
 void
 gdbpy_initialize_values (void)
 {
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 5bd2a4b..daaed2c 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -80,6 +80,17 @@ extern PyTypeObject value_object_type;
 extern PyTypeObject block_object_type;
 extern PyTypeObject symbol_object_type;
 
+typedef struct
+{
+  PyObject_HEAD
+
+  /* The thread we represent.  */
+  struct thread_info *thread;
+
+  /* The Inferior object to which this thread belongs.  */
+  PyObject *inf_obj;
+} thread_object;
+
 extern struct cmd_list_element *set_python_list;
 extern struct cmd_list_element *show_python_list;
 
@@ -92,6 +103,8 @@ PyObject *gdbpy_block_for_pc (PyObject *self, PyObject *args);
 PyObject *gdbpy_lookup_type (PyObject *self, PyObject *args, PyObject *kw);
 PyObject *gdbpy_create_lazy_string_object (CORE_ADDR address, long length,
 					   const char *encoding, struct type *type);
+PyObject *gdbpy_inferiors (PyObject *unused, PyObject *unused2);
+PyObject *gdbpy_selected_thread (PyObject *self, PyObject *args);
 PyObject *gdbpy_string_to_argv (PyObject *self, PyObject *args);
 PyObject *gdbpy_get_hook_function (const char *);
 PyObject *gdbpy_parameter (PyObject *self, PyObject *args);
@@ -106,6 +119,7 @@ PyObject *symbol_to_symbol_object (struct symbol *sym);
 PyObject *block_to_block_object (struct block *block, struct objfile *objfile);
 PyObject *value_to_value_object (struct value *v);
 PyObject *type_to_type_object (struct type *);
+PyObject *frame_info_to_frame_object (struct frame_info *frame);
 
 PyObject *pspace_to_pspace_object (struct program_space *);
 PyObject *pspy_get_printers (PyObject *, void *);
@@ -113,6 +127,10 @@ PyObject *pspy_get_printers (PyObject *, void *);
 PyObject *objfile_to_objfile_object (struct objfile *);
 PyObject *objfpy_get_printers (PyObject *, void *);
 
+thread_object *create_thread_object (struct thread_info *tp);
+thread_object *find_thread_object (ptid_t ptid);
+PyObject *find_inferior_object (int pid);
+
 struct block *block_object_to_block (PyObject *obj);
 struct symbol *symbol_object_to_symbol (PyObject *obj);
 struct value *value_object_to_value (PyObject *self);
@@ -136,6 +154,8 @@ void gdbpy_initialize_objfile (void);
 void gdbpy_initialize_breakpoints (void);
 void gdbpy_initialize_lazy_string (void);
 void gdbpy_initialize_parameters (void);
+void gdbpy_initialize_thread (void);
+void gdbpy_initialize_inferior (void);
 
 struct cleanup *make_cleanup_py_decref (PyObject *py);
 
@@ -188,6 +208,8 @@ gdb_byte *gdbpy_extract_lazy_string (PyObject *string,
 				     struct type **str_type, 
 				     long *length, char **encoding);
 
+int gdbpy_is_value_object (PyObject *obj);
+
 /* Note that these are declared here, and not in python.h with the
    other pretty-printer functions, because they refer to PyObject.  */
 PyObject *apply_varobj_pretty_printer (PyObject *print_obj,
@@ -204,4 +226,6 @@ extern PyObject *gdbpy_enabled_cst;
 
 extern PyObject *gdbpy_gdberror_exc;
 
+int get_addr_from_python (PyObject *obj, CORE_ADDR *addr);
+
 #endif /* GDB_PYTHON_INTERNAL_H */
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 31880c1..041977c 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -28,6 +28,8 @@
 #include "value.h"
 #include "language.h"
 #include "exceptions.h"
+#include "inferior.h"
+#include "gdbthread.h"
 
 #include <ctype.h>
 
@@ -686,6 +688,8 @@ Enables or disables printing of Python stack traces."),
   gdbpy_initialize_objfile ();
   gdbpy_initialize_breakpoints ();
   gdbpy_initialize_lazy_string ();
+  gdbpy_initialize_thread ();
+  gdbpy_initialize_inferior ();
 
   PyRun_SimpleString ("import gdb");
   PyRun_SimpleString ("gdb.pretty_printers = []");
@@ -799,7 +803,12 @@ Arguments are separate by spaces and may be quoted."
     "Write a string using gdb's filtered stream." },
   { "flush", gdbpy_flush, METH_NOARGS,
     "Flush gdb's filtered stdout stream." },
-
+  { "selected_thread", gdbpy_selected_thread, METH_NOARGS,
+    "selected_thread () -> gdb.InferiorThread.\n\
+Return the selected thread object." },
+  { "inferiors", gdbpy_inferiors, METH_NOARGS,
+    "inferiors () -> (gdb.Inferior, ...).\n\
+Return a tuple containing all inferiors." },
   {NULL, NULL, 0, NULL}
 };
 
diff --git a/gdb/target.h b/gdb/target.h
index 870a1eb..d49006e 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -1384,6 +1384,21 @@ extern int target_search_memory (CORE_ADDR start_addr,
 #define target_set_permissions() \
   (*current_target.to_set_permissions) ()
 
+/* Drives target_search_memory to sweep through the specified search space,
+   possibly in several iterations (with one call to this function for each
+   iteration).  *START_ADDR is the address where the search starts, and is
+   updated to the next starting address to continue the search.
+   *SEARCH_SPACE_LEN is the amount of bytes which will be searched, and is
+   updated for the next iteration.  PATTERN_BUF holds the pattern to
+   be searched  for, PATTERN_LEN is the size of the pattern in bytes.
+   If a match is found, it's address is put in *FOUND_ADDR.
+
+   Returns 1 if found, 0 if not found, and -1 if there was an error
+   requiring halting of the search (e.g. memory read error).  */
+int search_memory (CORE_ADDR *start_addr, ULONGEST *search_space_len,
+		   const char *pattern_buf, ULONGEST pattern_len,
+		   CORE_ADDR *found_addr);
+
 /* Command logging facility.  */
 
 #define target_log_command(p)						\
diff --git a/gdb/testsuite/gdb.python/Makefile.in b/gdb/testsuite/gdb.python/Makefile.in
index c6a2678..660941d 100644
--- a/gdb/testsuite/gdb.python/Makefile.in
+++ b/gdb/testsuite/gdb.python/Makefile.in
@@ -2,7 +2,7 @@ VPATH = @srcdir@
 srcdir = @srcdir@
 
 EXECUTABLES = py-type py-value py-prettyprint py-template py-block \
-		py-symbol py-mi py-breakpoint
+		py-symbol py-mi py-breakpoint py-inferior py-infthread
 
 all info install-info dvi install uninstall installcheck check:
 	@echo "Nothing to be done for $@..."
diff --git a/gdb/testsuite/gdb.python/py-inferior.c b/gdb/testsuite/gdb.python/py-inferior.c
new file mode 100644
index 0000000..0b48299
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-inferior.c
@@ -0,0 +1,49 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.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;
+
+
+int f2 (int a)
+{
+  char *str = "hello, testsuite";
+
+  puts (str);	/* Break here.  */
+
+  return ++a;
+}
+
+int f1 (int a, int b)
+{
+  return f2(a) + b;
+}
+
+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 (int argc, char *argv[])
+{
+  init_bufs ();
+
+  return f1 (1, 2);
+}
diff --git a/gdb/testsuite/gdb.python/py-inferior.exp b/gdb/testsuite/gdb.python/py-inferior.exp
new file mode 100644
index 0000000..27f91a7
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-inferior.exp
@@ -0,0 +1,202 @@
+# Copyright (C) 2009, 2010 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 file is part of the GDB testsuite.  It tests the mechanism
+# exposing inferiors to Python.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+# Run a command in GDB, and report a failure if a Python exception is thrown.
+# If report_pass is true, report a pass if no exception is thrown.
+proc gdb_py_test_silent_cmd {cmd name report_pass} {
+    global gdb_prompt
+
+  gdb_test_multiple $cmd $name {
+      -re "Traceback.*$gdb_prompt $"  { fail $name }
+      -re "$gdb_prompt $"	      { if $report_pass { pass $name } }
+  }
+}
+
+set testfile "py-inferior"
+set srcfile ${testfile}.c
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
+    return -1
+}
+
+# Start with a fresh gdb.
+clean_restart ${testfile}
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+# The following tests require execution.
+
+if ![runto_main] then {
+    fail "Can't run to main"
+    return 0
+}
+
+runto [gdb_get_line_number "Break here."]
+
+# Test basic gdb.Inferior attributes and methods.
+
+gdb_py_test_silent_cmd "python inferiors = gdb.inferiors ()" "get inferiors list" 1
+gdb_test "python print inferiors" "\\(<gdb.Inferior object at 0x\[\[:xdigit:\]\]+>,\\)" "verify inferiors list"
+gdb_py_test_silent_cmd "python i0 = inferiors\[0\]" "get first inferior" 0
+
+gdb_test "python print 'result =', i0 == inferiors\[0\]" " = True" "test equality comparison (true)"
+gdb_test "python print 'result =', i0.num" " = \[0-9\]+" "test Inferior.num"
+gdb_test "python print 'result =', i0.pid" " = \[0-9\]+" "test Inferior.pid"
+gdb_test "python print 'result =', i0.was_attached" " = False" "test Inferior.was_attached"
+gdb_test "python print i0.threads ()" "\\(<gdb.InferiorThread object at 0x\[\[:xdigit:\]\]+>,\\)" "test Inferior.threads"
+
+# Test memory read and write operations.
+
+gdb_py_test_silent_cmd "python addr = gdb.selected_frame ().read_var ('str')" \
+  "read str address" 0
+gdb_py_test_silent_cmd "python str = gdb.inferiors()\[0\].read_memory (addr, 5)" \
+  "read str contents" 1
+gdb_py_test_silent_cmd "python str\[1\] = 'a'" "change str" 0
+gdb_py_test_silent_cmd "python gdb.inferiors()\[0\].write_memory (addr, str)" \
+  "write str" 1
+gdb_test "print str" " = 0x\[\[:xdigit:\]\]+ \"hallo, testsuite\"" \
+  "ensure str was changed in the inferior"
+
+# Test memory search.
+
+set hex_number {0x[0-9a-fA-F][0-9a-fA-F]*}
+set dec_number {[0-9]+}
+set history_prefix {[$][0-9]* = }
+set newline {[\r\n]+}
+set pattern_not_found "${newline}.]"
+set one_pattern_found "${newline}.${dec_number}L]"
+set two_patterns_found "${newline}.${dec_number}L, ${dec_number}L]"
+
+# Test string pattern.
+
+gdb_test "set *(int32_t*) &int8_search_buf\[10\] = 0x61616161" "" ""
+gdb_test "py search_buf = gdb.selected_frame ().read_var ('int8_search_buf')" "" ""
+gdb_test "py start_addr = search_buf.address" "" ""
+gdb_test "py length = search_buf.type.sizeof" "" ""
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, 'aaa')" \
+  "${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 "py print gdb.inferiors()\[0\].search_memory (start_addr, 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 "py print gdb.inferiors()\[0\].search_memory (start_addr, 10+3+1, \['a', 'a', 'a', 'a'\])" \
+  "${one_pattern_found}" "pattern found at end of range"
+
+# Test max-count with size, with different parameter position
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, \[0x61, 0x61\], 1, 1)" \
+  "${one_pattern_found}" "size = 1, max_count = 1"
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, \[0x61, 0x61\], 1, 2)" \
+  "${two_patterns_found}" "size = 1, max_count = 2, normal ordering"
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, \[0x61, 0x61\], size = 1, max_count = 2)" \
+  "${two_patterns_found}" "size = 1, max_count = 2, normal ordering, with keywords"
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, \[0x61, 0x61\], max_count = 2, size = 1)" \
+  "${two_patterns_found}" "size = 1, max_count = 2, inverted ordering"
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, \['a', 'a'\], max_count = 2)" \
+  "${two_patterns_found}" "max_count = 2, with keyword"
+
+# Test 16-bit pattern.
+
+gdb_test "set int16_search_buf\[10\] = 0x1234" "" ""
+gdb_test "py search_buf = gdb.selected_frame ().read_var ('int16_search_buf')" "" ""
+gdb_test "py start_addr = search_buf.address" "" ""
+gdb_test "py length = search_buf.type.sizeof" "" ""
+gdb_test "py pattern = gdb.parse_and_eval ('(int16_t) 0x1234')" "" ""
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, 0x1234, 2)" \
+  "${one_pattern_found}" "find 16-bit pattern, with python pattern"
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, pattern)" \
+  "${one_pattern_found}" "find 16-bit pattern, with value pattern"
+
+# Test 32-bit pattern.
+
+gdb_test "set int32_search_buf\[10\] = 0x12345678" "" ""
+gdb_test "py search_buf = gdb.selected_frame ().read_var ('int32_search_buf')" "" ""
+gdb_test "py start_addr = search_buf.address" "" ""
+gdb_test "py length = search_buf.type.sizeof" "" ""
+gdb_test "py pattern = gdb.parse_and_eval ('(int32_t) 0x12345678')" "" ""
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, 0x12345678, 4)" \
+  "${one_pattern_found}" "find 32-bit pattern, with python pattern"
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, pattern)" \
+  "${one_pattern_found}" "find 32-bit pattern, with value pattern"
+
+# Test 64-bit pattern.
+
+gdb_test "set int64_search_buf\[10\] = 0xfedcba9876543210LL" "" ""
+gdb_test "py search_buf = gdb.selected_frame ().read_var ('int64_search_buf')" "" ""
+gdb_test "py start_addr = search_buf.address" "" ""
+gdb_test "py length = search_buf.type.sizeof" "" ""
+gdb_test "py pattern = gdb.parse_and_eval ('(int64_t) 0xfedcba9876543210LL')" "" ""
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, 0xfedcba9876543210, 8)" \
+  "${one_pattern_found}" "find 64-bit pattern, with python pattern"
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, pattern)" \
+  "${one_pattern_found}" "find 64-bit pattern, with value 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 "py search_buf = gdb.selected_frame ().read_var ('search_buf')" "" ""
+gdb_test "py start_addr = search_buf\[0\].address" "" ""
+gdb_test "py pattern1 = gdb.parse_and_eval ('(int8_t) 0x62')" "" ""
+gdb_test "py pattern2 = gdb.parse_and_eval ('(int16_t) 0x6363')" "" ""
+gdb_test "py pattern3 = gdb.parse_and_eval ('(int32_t) 0x64646464')" "" ""
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, 100, \[pattern1, pattern2, pattern3\])" \
+    "${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 ;
+
+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 "py start_addr = gdb.selected_frame ().read_var ('search_buf')" "" ""
+gdb_test "py length = gdb.selected_frame ().read_var ('search_buf_size')" "" ""
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, 0x12345678, 4)" \
+  "${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 "py print gdb.inferiors()\[0\].search_memory (start_addr, length, 0xfdb97531, 4)" \
+      "${one_pattern_found}" "find pattern straddling chunk boundary"
+}
diff --git a/gdb/testsuite/gdb.python/py-infthread.c b/gdb/testsuite/gdb.python/py-infthread.c
new file mode 100644
index 0000000..22eb9f2
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-infthread.c
@@ -0,0 +1,14 @@
+int f2 (int a)
+{
+  return ++a;
+}
+
+int f1 (int a, int b)
+{
+  return f2(a) + b;
+}
+
+int main (int argc, char *argv[])
+{
+  return f1 (1, 2);
+}
diff --git a/gdb/testsuite/gdb.python/py-infthread.exp b/gdb/testsuite/gdb.python/py-infthread.exp
new file mode 100644
index 0000000..a25d0b5
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-infthread.exp
@@ -0,0 +1,53 @@
+# Copyright (C) 2009, 2010 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 file is part of the GDB testsuite.  It tests the mechanism
+# exposing inferior threads to Python.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+set testfile "py-infthread"
+set srcfile ${testfile}.c
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
+    return -1
+}
+
+# Start with a fresh gdb.
+clean_restart ${testfile}
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+# The following tests require execution.
+
+if ![runto_main] then {
+    fail "Can't run to main"
+    return 0
+}
+
+runto [gdb_get_line_number "Break here."]
+
+# Test basic gdb.Inferior attributes and methods.
+
+gdb_py_test_silent_cmd "python t0 = gdb.selected_thread ()" "test gdb.selected_thread" 1
+gdb_test "python print t0" "\\<gdb.InferiorThread object at 0x\[\[:xdigit:\]\]+>" "verify InferiorThread object"
+gdb_test "python print 'result =', t0.num" " = \[0-9\]+" "test Inferior.num"
+gdb_test "python print 'result =', t0.ptid" " = \\(\[0-9\]+, \[0-9\]+, \[0-9\]+\\)" "test InferiorThread.ptid"
+
+gdb_test "python print 'result =', t0.is_stopped ()" " = True" "test InferiorThread.is_stopped"
+gdb_test "python print 'result =', t0.is_running ()" " = False" "test InferiorThread.is_running"
+gdb_test "python print 'result =', t0.is_exited ()" " = False" "test InferiorThread.is_exited"
diff --git a/gdb/value.c b/gdb/value.c
index 5e0e8d8..8812fe1 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -2244,6 +2244,43 @@ pack_long (gdb_byte *buf, struct type *type, LONGEST num)
 }
 
 
+/* Pack NUM into BUF using a target format of TYPE.  */
+
+void
+pack_unsigned_long (gdb_byte *buf, struct type *type, ULONGEST num)
+{
+  int len;
+  enum bfd_endian byte_order;
+
+  type = check_typedef (type);
+  len = TYPE_LENGTH (type);
+  byte_order = gdbarch_byte_order (get_type_arch (type));
+
+  switch (TYPE_CODE (type))
+    {
+    case TYPE_CODE_INT:
+    case TYPE_CODE_CHAR:
+    case TYPE_CODE_ENUM:
+    case TYPE_CODE_FLAGS:
+    case TYPE_CODE_BOOL:
+    case TYPE_CODE_RANGE:
+    case TYPE_CODE_MEMBERPTR:
+      store_unsigned_integer (buf, len, byte_order, num);
+      break;
+
+    case TYPE_CODE_REF:
+    case TYPE_CODE_PTR:
+      store_typed_address (buf, type, (CORE_ADDR) num);
+      break;
+
+    default:
+      error (_("\
+Unexpected type (%d) encountered for unsigned integer constant."),
+	     TYPE_CODE (type));
+    }
+}
+
+
 /* Convert C numbers into newly allocated values.  */
 
 struct value *
@@ -2256,6 +2293,19 @@ value_from_longest (struct type *type, LONGEST num)
 }
 
 
+/* Convert C unsigned numbers into newly allocated values.  */
+
+struct value *
+value_from_ulongest (struct type *type, ULONGEST num)
+{
+  struct value *val = allocate_value (type);
+
+  pack_unsigned_long (value_contents_raw (val), type, num);
+
+  return val;
+}
+
+
 /* Create a value representing a pointer of type TYPE to the address
    ADDR.  */
 struct value *
diff --git a/gdb/value.h b/gdb/value.h
index c3dfb17..474e661 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -366,6 +366,7 @@ extern LONGEST unpack_field_as_long (struct type *type,
 extern void pack_long (gdb_byte *buf, struct type *type, LONGEST num);
 
 extern struct value *value_from_longest (struct type *type, LONGEST num);
+extern struct value *value_from_ulongest (struct type *type, ULONGEST num);
 extern struct value *value_from_pointer (struct type *type, CORE_ADDR addr);
 extern struct value *value_from_double (struct type *type, DOUBLEST num);
 extern struct value *value_from_decfloat (struct type *type,


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [python][patch] Inferior and Thread information support.
  2010-06-18  6:49   ` Phil Muldoon
@ 2010-06-18 14:21     ` Doug Evans
  2010-06-18 15:47       ` Phil Muldoon
  2010-06-18 18:04     ` Tom Tromey
  1 sibling, 1 reply; 21+ messages in thread
From: Doug Evans @ 2010-06-18 14:21 UTC (permalink / raw)
  To: Phil Muldoon; +Cc: tromey, gdb-patches ml

Hi.  Just a few comments.

On Thu, Jun 17, 2010 at 11:48 PM, Phil Muldoon <pmuldoon@redhat.com> wrote:
> +
> +@findex gdb.read_memory
> +@defmethod Inferior read_memory address length
> +Read @var{length} bytes of memory from the inferior, starting at
> +@var{address}.  Returns a buffer object, which behaves much like an array
> +or a string.  It can be modified and given to the @code{gdb.write_memory}
> +function.
> +@end defmethod
> +
> +@findex gdb.write_memory
> +@defmethod Inferior write_memory address buffer @r{[}length@r{]}
> +Write the contents of @var{buffer} to the inferior, starting at
> +@var{address}.  The @var{buffer} parameter must be a Python object
> +which supports the buffer protocol, i.e., a string, an array or the
> +object returned from @code{gdb.read_memory}.  If given, @var{length}
> +determines the number of bytes from @var{buffer} to be written.
> +@end defmethod
> +
> +@findex gdb.search_memory
> +@defmethod Inferior search_memory address length pattern @r{[}size@r{]} @r{[}max_count@r{]}
> +Search a region of the inferior memory starting at @var{address} with the
> +given @var{length}.  @var{pattern} can be a string, a byte array, a buffer
> +object, a number, a @code{gdb.Value} object (@pxref{Values From
> +Inferior}), or a Python list or a tuple with elements in any combination
> +of those types.  If @var{pattern} is a list or a tuple, each element
> +will be extracted and concatenated together to form @var{pattern}.
> +
> +If @var{size} is given and is non-zero, it specifies the size in bytes
> +of a Python scalar or @code{gdb.Value} in the search pattern.  If
> +@var{size} is zero or not specified, the size is taken from the value's type
> +in the current language.  If @var{pattern} is a tuple or list, these
> +actions apply to each element in the tuple or list.  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.  @var{max_count} is the highest
> +number of matches to search for.
> +@end defmethod
> +@end table

I think search_memory should take just the same type of parameter for
the search pattern that write_memory takes: a byte array (etc.).  Keep
the API simple and let the caller do the conversion to a byte array
(or buffer protocol).
We could provide utilities to convert to/from byte arrays (then
write_memory can use them too!) but Python probably has them already.
Then you can punt on the `size' parameter completely.

You also want to document what the result is.

> +@defivar InferiorThread ptid
> +ID of the thread, as assigned by the operating system.  This attribute is a
> +tuple containing three integers.  The first is the Process ID (PID); the second
> +is the Lightweight Process ID (TID), and the third is the Thread ID (TID).
> +Either the TID or TID may be 0, which indicates that the operating system
> +does not  use that identifier.
> +@end defivar

A couple of typos here: TID is duplicated.

> +# Test max-count with size, with different parameter position
> +
> +gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, \[0x61, 0x61\], 1, 1)" \
> +  "${one_pattern_found}" "size = 1, max_count = 1"
> +
> +gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, \[0x61, 0x61\], 1, 2)" \
> +  "${two_patterns_found}" "size = 1, max_count = 2, normal ordering"
> +
> +gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, \[0x61, 0x61\], size = 1, max_count = 2)" \
> +  "${two_patterns_found}" "size = 1, max_count = 2, normal ordering, with keywords"
> +
> +gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, \[0x61, 0x61\], max_count = 2, size = 1)" \
> +  "${two_patterns_found}" "size = 1, max_count = 2, inverted ordering"
> +
> +gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, \['a', 'a'\], max_count = 2)" \
> +  "${two_patterns_found}" "max_count = 2, with keyword"

The CLI testcase for this tests the parameter ordering in order to
test the parsing of the parameters, but you don't need to do that here
(we can trust python on this).  Doesn't hurt though.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [python][patch] Inferior and Thread information support.
  2010-06-18 14:21     ` Doug Evans
@ 2010-06-18 15:47       ` Phil Muldoon
  2010-06-18 17:59         ` Tom Tromey
  0 siblings, 1 reply; 21+ messages in thread
From: Phil Muldoon @ 2010-06-18 15:47 UTC (permalink / raw)
  To: Doug Evans; +Cc: tromey, gdb-patches ml

On 06/18/2010 03:21 PM, Doug Evans wrote:

> I think search_memory should take just the same type of parameter for
> the search pattern that write_memory takes: a byte array (etc.).  Keep
> the API simple and let the caller do the conversion to a byte array
> (or buffer protocol).
> We could provide utilities to convert to/from byte arrays (then
> write_memory can use them too!) but Python probably has them already.
> Then you can punt on the `size' parameter completely.

I'm not sure that conversion from a Python type to a buffer object are
that transparent.  For instance there seems to be no straightforward
way to represent 3.141 as a sequence of bytes backed by a buffer in
Python (or accessible via the buffer interface rather).  Maybe there
is, I'm certainly not a Python language expert.  I hope someone can
prove me wrong!


Right now with the existing code, we take 3.141 as a gdb.value and
convert that to bytes via value_contents. Your suggestions would
certainly make the existing code simpler (and my porting task a little
easier ;), but I can't help thinking that it would be just making the
user jump through extra hoops just for API pureness.  I strive for
that, it's a good thing; it just strikes me a little too much in this
case.  OTOH we could just make add_value_pattern available via the API
and have the user manually do the conversion "the GDB way".

> 
> You also want to document what the result is.

Sure, will do. 

Cheers,

Phil

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [python][patch] Inferior and Thread information support.
  2010-06-18 15:47       ` Phil Muldoon
@ 2010-06-18 17:59         ` Tom Tromey
  2010-06-18 20:10           ` Phil Muldoon
  0 siblings, 1 reply; 21+ messages in thread
From: Tom Tromey @ 2010-06-18 17:59 UTC (permalink / raw)
  To: Phil Muldoon; +Cc: Doug Evans, gdb-patches ml

>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:

Phil> I'm not sure that conversion from a Python type to a buffer object are
Phil> that transparent.  For instance there seems to be no straightforward
Phil> way to represent 3.141 as a sequence of bytes backed by a buffer in
Phil> Python (or accessible via the buffer interface rather).  Maybe there
Phil> is, I'm certainly not a Python language expert.  I hope someone can
Phil> prove me wrong!

Yeah, there are some things in Python for this.  See the 'struct'
module.

I think it is somewhat better to keep our API simple and rely on the
built-in library for more complicated things.  Sorry about that.

Phil> Right now with the existing code, we take 3.141 as a gdb.value and
Phil> convert that to bytes via value_contents. Your suggestions would
Phil> certainly make the existing code simpler (and my porting task a little
Phil> easier ;), but I can't help thinking that it would be just making the
Phil> user jump through extra hoops just for API pureness.  I strive for
Phil> that, it's a good thing; it just strikes me a little too much in this
Phil> case.  OTOH we could just make add_value_pattern available via the API
Phil> and have the user manually do the conversion "the GDB way".

Arguably, gdb.Value should support the buffer protocol.  I don't know if
that is directly possible, but if not we should supply a way to convert
a Value to a buffer -- to give the user a way to view the underlying
bits.

I think you should just remove the max_count argument.  It seems weird
to me.  If people want multiple searches, it is easy to iterate.  Or, if
we really want to support multiple searches, then I think we should do
it by returning an iterator instead of a list.

Tom

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [python][patch] Inferior and Thread information support.
  2010-06-18  6:49   ` Phil Muldoon
  2010-06-18 14:21     ` Doug Evans
@ 2010-06-18 18:04     ` Tom Tromey
  2010-06-22 10:32       ` Phil Muldoon
  1 sibling, 1 reply; 21+ messages in thread
From: Tom Tromey @ 2010-06-18 18:04 UTC (permalink / raw)
  To: Phil Muldoon; +Cc: gdb-patches ml

>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:

Phil> +int
Phil> +search_memory (CORE_ADDR *start_addr, ULONGEST *search_space_len,
Phil> +	       const char *pattern_buf, ULONGEST pattern_len,
Phil> +	       CORE_ADDR *found_addr)

I think if you remove the max_count parameter then you won't need this
at all, you can just call target_search_memory directly.

Phil> +struct inflist_entry {
Phil> +  inferior_object *inf_obj;
Phil> +  struct inflist_entry *next;
Phil> +};
[...]
Phil> +/* List containing inferior_objects.  This list owns a reference to each
Phil> +   object it contains.  */
Phil> +static struct inflist_entry *gdbpy_inferior_list;
Phil> +
Phil> +static int ninferiors;

I'm sorry I missed this earlier...

struct inferior now has a "user-data" API, like objfiles do.
See register_inferior_data and register_inferior_data_with_cleanup.

I think this means that we can attach the Python objects directly to the
struct inferior, rather than keeping an external list.

This change should simplify a lot of code.

Tom

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [python][patch] Inferior and Thread information support.
  2010-06-18 17:59         ` Tom Tromey
@ 2010-06-18 20:10           ` Phil Muldoon
  2010-06-25 20:41             ` Tom Tromey
  0 siblings, 1 reply; 21+ messages in thread
From: Phil Muldoon @ 2010-06-18 20:10 UTC (permalink / raw)
  To: tromey; +Cc: Doug Evans, gdb-patches ml

On 06/18/2010 06:58 PM, Tom Tromey wrote:
>>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:
> 
> Phil> I'm not sure that conversion from a Python type to a buffer object are
> Phil> that transparent.  For instance there seems to be no straightforward
> Phil> way to represent 3.141 as a sequence of bytes backed by a buffer in
> Phil> Python (or accessible via the buffer interface rather).  Maybe there
> Phil> is, I'm certainly not a Python language expert.  I hope someone can
> Phil> prove me wrong!
> 
> Yeah, there are some things in Python for this.  See the 'struct'
> module.
> 
> I think it is somewhat better to keep our API simple and rely on the
> built-in library for more complicated things.  Sorry about that.

Simplifying the API will be the easy part (though I  disagree on it),
it just means removing code.  I'll submit a patch for that next week.

 
> 
> Phil> Right now with the existing code, we take 3.141 as a gdb.value and
> Phil> convert that to bytes via value_contents. Your suggestions would
> Phil> certainly make the existing code simpler (and my porting task a little
> Phil> easier ;), but I can't help thinking that it would be just making the
> Phil> user jump through extra hoops just for API pureness.  I strive for
> Phil> that, it's a good thing; it just strikes me a little too much in this
> Phil> case.  OTOH we could just make add_value_pattern available via the API
> Phil> and have the user manually do the conversion "the GDB way".
> 
> Arguably, gdb.Value should support the buffer protocol.  I don't know if
> that is directly possible, but if not we should supply a way to convert
> a Value to a buffer -- to give the user a way to view the underlying
> bits.

I don't disagree in principle.  I'm not sure this is within the
the scope of this patch though?
 
> I think you should just remove the max_count argument.  It seems weird
> to me.  If people want multiple searches, it is easy to iterate.  Or, if
> we really want to support multiple searches, then I think we should do
> it by returning an iterator instead of a list.

From a pragmatic point of view, between you and Doug I'm a little lost
about what we need to do to get this patch suitable for inclusion in
the FSF tree, with a possible view for inclusion to 7.2?  I'm not sure
what the removal of the max_count has to do with the other comments?
Making search_memory only support buffers is okay from the Python
side, that is easy.  What else needs to be done is my question ;)

Cheers,

Phil



^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [python][patch] Inferior and Thread information support.
  2010-06-18 18:04     ` Tom Tromey
@ 2010-06-22 10:32       ` Phil Muldoon
  2010-06-25 20:38         ` Tom Tromey
  0 siblings, 1 reply; 21+ messages in thread
From: Phil Muldoon @ 2010-06-22 10:32 UTC (permalink / raw)
  To: tromey; +Cc: gdb-patches ml

On 06/18/2010 07:04 PM, Tom Tromey wrote:
>>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:
> 
> Phil> +int
> Phil> +search_memory (CORE_ADDR *start_addr, ULONGEST *search_space_len,
> Phil> +	       const char *pattern_buf, ULONGEST pattern_len,
> Phil> +	       CORE_ADDR *found_addr)
> 
> I think if you remove the max_count parameter then you won't need this
> at all, you can just call target_search_memory directly.

OK.

> struct inferior now has a "user-data" API, like objfiles do.
> See register_inferior_data and register_inferior_data_with_cleanup.
> 
> I think this means that we can attach the Python objects directly to the
> struct inferior, rather than keeping an external list.

I've done this.  Threads do not have the same registry mechanism yet, so
I've had to keep the old observer mechanism there.  But inferiors use
the new registry cleanup.

I've also simplified the search_memory api as requested.  We now take
an address, a length and a buffer only.  We now only return the first
match found in the buffer. (and let the user do the iteration in the
scripting side).

Latest patch attached. WDYT?

Cheers

Phil

--

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 5cb02d8..6dbb284 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -275,6 +275,8 @@ SUBDIR_PYTHON_OBS = \
 	py-cmd.o \
 	py-frame.o \
 	py-function.o \
+	py-inferior.o \
+	py-infthread.o \
 	py-lazy-string.o \
 	py-objfile.o \
 	py-param.o \
@@ -293,6 +295,8 @@ SUBDIR_PYTHON_SRCS = \
 	python/py-cmd.c \
 	python/py-frame.c \
 	python/py-function.c \
+	python/py-inferior.c \
+	python/py-infthread.c \
 	python/py-lazy-string.c \
 	python/py-objfile.c \
 	python/py-param.c \
@@ -2018,6 +2022,14 @@ py-function.o: $(srcdir)/python/py-function.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-function.c
 	$(POSTCOMPILE)
 
+py-inferior.o: $(srcdir)/python/py-inferior.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-inferior.c
+	$(POSTCOMPILE)
+
+py-infthread.o: $(srcdir)/python/py-infthread.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-infthread.c
+	$(POSTCOMPILE)
+
 py-lazy-string.o: $(srcdir)/python/py-lazy-string.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-lazy-string.c
 	$(POSTCOMPILE)
diff --git a/gdb/NEWS b/gdb/NEWS
index af65274..d28bae4 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -100,9 +100,9 @@ is now deprecated.
 * Python scripting
 
 ** The GDB Python API now has access to breakpoints, symbols, symbol
-   tables, program spaces, and frame's code blocks.  Additionally, GDB
-   Parameters can now be created from the API, and manipulated via
-   set/show in the CLI.
+   tables, program spaces, inferiors, threads and frame's code blocks.
+   Additionally, GDB Parameters can now be created from the API, and
+   manipulated via set/show in the CLI.
 
 ** New functions gdb.target_charset, gdb.target_wide_charset,
    gdb.progspaces, gdb.current_progspace, and gdb.string_to_argv.
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 463030f..553516d 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -20170,6 +20170,8 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
 * Pretty Printing API::         Pretty-printing values.
 * Selecting Pretty-Printers::   How GDB chooses a pretty-printer.
 * Disabling Pretty-Printers::   Disabling broken printers.
+* Inferiors In Python::         Python representation of inferiors (processes)
+* Threads In Python::           Accessing inferior threads from Python.
 * Commands In Python::          Implementing new commands in Python.
 * Parameters In Python::        Adding new @value{GDBN} parameters.
 * Functions In Python::         Writing new convenience functions.
@@ -20986,6 +20988,137 @@ attribute to the registered function or callable object.  If this attribute
 is present and its value is @code{False}, the printer is disabled, otherwise
 the printer is enabled.
 
+@node Inferiors In Python
+@subsubsection Inferiors In Python
+@cindex inferiors in python
+
+@findex gdb.Inferior
+Programs which are being run under @value{GDBN} are called inferiors
+(@pxref{Inferiors and Programs}).  Python scripts can access
+information about and manipulate inferiors controlled by @value{GDBN}
+via objects of the @code{gdb.Inferior} class.
+
+The following inferior-related functions are available in the @code{gdb}
+module:
+
+@defun inferiors
+Return a tuple containing all inferior objects.
+@end defun
+
+A @code{gdb.Inferior} object has the following attributes:
+
+@table @code
+@defivar Inferior num
+ID of inferior, as assigned by GDB.
+@end defivar
+
+@defivar Inferior pid
+Process ID of the inferior, as assigned by the underlying operating
+system.
+@end defivar
+
+@defivar Inferior was_attached
+Boolean signaling whether the inferior was created using `attach', or
+started by @value{GDBN} itself.
+@end defivar
+@end table
+
+A @code{gdb.Inferior} object has the following methods:
+
+@table @code
+@defmethod Inferior threads
+This method returns a tuple holding all the threads which are valid
+when it is called.  If there are no valid threads, the method will
+return an empty tuple.
+@end defmethod
+
+@findex gdb.read_memory
+@defmethod Inferior read_memory address length
+Read @var{length} bytes of memory from the inferior, starting at
+@var{address}.  Returns a buffer object, which behaves much like an array
+or a string.  It can be modified and given to the @code{gdb.write_memory}
+function.
+@end defmethod
+
+@findex gdb.write_memory
+@defmethod Inferior write_memory address buffer @r{[}length@r{]}
+Write the contents of @var{buffer} to the inferior, starting at
+@var{address}.  The @var{buffer} parameter must be a Python object
+which supports the buffer protocol, i.e., a string, an array or the
+object returned from @code{gdb.read_memory}.  If given, @var{length}
+determines the number of bytes from @var{buffer} to be written.
+@end defmethod
+
+@findex gdb.search_memory
+@defmethod Inferior search_memory address length pattern
+Search a region of the inferior memory starting at @var{address} with
+the given @var{length} using the search pattern supplied in
+@var{pattern}.  The @var{pattern} parameter must be a Python object
+which supports the buffer protocol, i.e., a string, an array or the
+object returned from @code{gdb.read_memory}.  Returns a Python @code{Long}
+containing the address where the pattern was found, or @code{None} if
+the pattern could not be found.
+@end defmethod
+@end table
+
+@node Threads In Python
+@subsubsection Threads In Python
+@cindex threads in python
+
+@findex gdb.InferiorThread
+Python scripts can access information about, and manipulate inferior threads
+controlled by @value{GDBN}, via objects of the @code{gdb.InferiorThread} class.
+
+The following thread-related functions are available in the @code{gdb}
+module:
+
+@findex gdb.selected_thread
+@defun selected_thread
+This function returns the thread object for the selected thread.  If there
+is no selected thread, this will return @code{None}.
+@end defun
+
+A @code{gdb.InferiorThread} object has the following attributes:
+
+@table @code
+@defivar InferiorThread num
+ID of the thread, as assigned by GDB.
+@end defivar
+
+@defivar InferiorThread ptid
+ID of the thread, as assigned by the operating system.  This attribute is a
+tuple containing three integers.  The first is the Process ID (PID); the second
+is the Lightweight Process ID (LWPID), and the third is the Thread ID (TID).
+Either the LWPID or TID may be 0, which indicates that the operating system
+does not  use that identifier.
+@end defivar
+@end table
+
+A @code{gdb.InferiorThread} object has the following methods:
+
+@table @code
+@defmethod InferiorThread newest_frame
+Return the newest frame thread's stack.
+@end defmethod
+
+@defmethod InferiorThread switch
+This changes @value{GDBN}'s currently selected thread to the one represented
+by this object.
+@end defmethod
+
+@defmethod InferiorThread is_stopped
+Return a Boolean indicating whether the thread is stopped.
+@end defmethod
+
+@defmethod InferiorThread is_running
+Return a Boolean indicating whether the thread is running.
+@end defmethod
+
+@defmethod InferiorThread is_exited
+Return a Boolean indicating whether the thread is exited.
+@end defmethod
+@end table
+
 @node Commands In Python
 @subsubsection Commands In Python
 
diff --git a/gdb/python/py-frame.c b/gdb/python/py-frame.c
index 6e9e5d3..a7df810 100644
--- a/gdb/python/py-frame.c
+++ b/gdb/python/py-frame.c
@@ -265,7 +265,7 @@ frapy_function (PyObject *self, PyObject *args)
 /* Convert a frame_info struct to a Python Frame object.
    Sets a Python exception and returns NULL on error.  */
 
-static frame_object *
+PyObject *
 frame_info_to_frame_object (struct frame_info *frame)
 {
   frame_object *frame_obj;
@@ -296,7 +296,7 @@ frame_info_to_frame_object (struct frame_info *frame)
 
   frame_obj->gdbarch = get_frame_arch (frame);
 
-  return frame_obj;
+  return (PyObject *) frame_obj;
 }
 
 /* Implementation of gdb.Frame.older (self) -> gdb.Frame.
@@ -497,7 +497,7 @@ PyObject *
 gdbpy_selected_frame (PyObject *self, PyObject *args)
 {
   struct frame_info *frame;
-  frame_object *frame_obj = NULL;   /* Initialize to appease gcc warning.  */
+  PyObject *frame_obj = NULL;   /* Initialize to appease gcc warning.  */
   volatile struct gdb_exception except;
 
   TRY_CATCH (except, RETURN_MASK_ALL)
@@ -507,7 +507,7 @@ gdbpy_selected_frame (PyObject *self, PyObject *args)
     }
   GDB_PY_HANDLE_EXCEPTION (except);
 
-  return (PyObject *) frame_obj;
+  return frame_obj;
 }
 
 /* Implementation of gdb.stop_reason_string (Integer) -> String.
diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c
new file mode 100644
index 0000000..113e8ef
--- /dev/null
+++ b/gdb/python/py-inferior.c
@@ -0,0 +1,732 @@
+/* Python interface to inferiors.
+
+   Copyright (C) 2009, 2010 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 "exceptions.h"
+#include "gdbcore.h"
+#include "gdbthread.h"
+#include "inferior.h"
+#include "observer.h"
+#include "python-internal.h"
+#include "arch-utils.h"
+#include "language.h"
+
+struct threadlist_entry {
+  thread_object *thread_obj;
+  struct threadlist_entry *next;
+};
+
+typedef struct
+{
+  PyObject_HEAD
+
+  /* The inferior we represent.  */
+  struct inferior *inferior;
+
+  /* thread_object instances under this inferior.  This list owns a
+     reference to each object it contains.  */
+  struct threadlist_entry *threads;
+
+  /* Number of threads in the list.  */
+  int nthreads;
+} inferior_object;
+
+static PyTypeObject inferior_object_type;
+
+static const struct inferior_data *infpy_inf_data_key;
+
+typedef struct {
+  PyObject_HEAD
+  void *buffer;
+
+  /* These are kept just for mbpy_str.  */
+  CORE_ADDR addr;
+  CORE_ADDR length;
+} membuf_object;
+
+static PyTypeObject membuf_object_type;
+
+/* Require that INFERIOR be a valid inferior ID.  */
+#define INFPY_REQUIRE_VALID(Inferior)				\
+  do {								\
+    if (!Inferior->inferior)					\
+      {								\
+	PyErr_SetString (PyExc_RuntimeError,			\
+			 _("Inferior no longer exists."));	\
+	return NULL;						\
+      }								\
+  } while (0)
+
+\f
+
+/* Return a Reference to the Python object of type Inferior
+   representing INFERIOR.  If the object has already been created,
+   return it.  Otherwise, create it.  Return NULL on failure.  */
+PyObject *
+inferior_to_inferior_object (struct inferior *inferior)
+{
+  inferior_object *inf_obj;
+
+  inf_obj = inferior_data (inferior, infpy_inf_data_key);
+  if (!inf_obj)
+    {
+      struct cleanup *cleanup;
+      cleanup = ensure_python_env (python_gdbarch, python_language);
+
+      inf_obj = PyObject_New (inferior_object, &inferior_object_type);
+      if (!inf_obj)
+	{
+	  do_cleanups (cleanup);
+	  return NULL;
+	}
+
+      inf_obj->inferior = inferior;
+      inf_obj->threads = NULL;
+      inf_obj->nthreads = 0;
+
+      set_inferior_data (inferior, infpy_inf_data_key, inf_obj);
+
+      do_cleanups (cleanup);
+    }
+  else
+    Py_INCREF (inf_obj);
+
+  return (PyObject *) inf_obj;
+}
+
+/* Finds the Python Inferior object for the given PID.  */
+PyObject *
+find_inferior_object (int pid)
+{
+  struct inflist_entry *p;
+  struct inferior *inf = find_inferior_pid (pid);
+
+  if (inf)
+    return inferior_to_inferior_object (inf);
+
+  return NULL;
+}
+
+thread_object *
+find_thread_object (ptid_t ptid)
+{
+  int pid;
+  struct threadlist_entry *thread;
+  PyObject *inf_obj;
+
+  pid = PIDGET (ptid);
+  inf_obj = find_inferior_object (pid);
+
+  if (inf_obj)
+    for (thread = ((inferior_object *)inf_obj)->threads; thread;
+	 thread = thread->next)
+      if (ptid_equal (thread->thread_obj->thread->ptid, ptid))
+	return thread->thread_obj;
+
+  return NULL;
+}
+\f
+
+static void
+add_thread_object (struct thread_info *tp)
+{
+  struct cleanup *cleanup;
+  thread_object *thread_obj;
+  inferior_object *inf_obj;
+  struct threadlist_entry *entry;
+
+  cleanup = ensure_python_env (python_gdbarch, python_language);
+
+  thread_obj = create_thread_object (tp);
+  if (!thread_obj)
+    {
+      warning (_("Cannot create Python InferiorThread object."));
+      gdbpy_print_stack ();
+      do_cleanups (cleanup);
+      return;
+    }
+
+  inf_obj = (inferior_object *) thread_obj->inf_obj;
+
+  entry = xmalloc (sizeof (struct threadlist_entry));
+  entry->thread_obj = thread_obj;
+  entry->next = inf_obj->threads;
+
+  inf_obj->threads = entry;
+  inf_obj->nthreads++;
+
+  do_cleanups (cleanup);
+}
+
+static void
+delete_thread_object (struct thread_info *tp, int ignore)
+{
+  struct cleanup *cleanup;
+  inferior_object *inf_obj;
+  thread_object *thread_obj;
+  struct threadlist_entry **entry, *tmp;
+
+  inf_obj = (inferior_object *) find_inferior_object (PIDGET(tp->ptid));
+  if (!inf_obj)
+    return;
+
+  /* Find thread entry in its inferior's thread_list.  */
+  for (entry = &inf_obj->threads; *entry != NULL; entry =
+	 &(*entry)->next)
+    if ((*entry)->thread_obj->thread == tp)
+      break;
+
+  if (!*entry)
+    return;
+
+  cleanup = ensure_python_env (python_gdbarch, python_language);
+
+  tmp = *entry;
+  tmp->thread_obj->thread = NULL;
+
+  *entry = (*entry)->next;
+  inf_obj->nthreads--;
+
+  Py_DECREF (tmp->thread_obj);
+  xfree (tmp);
+
+  do_cleanups (cleanup);
+}
+
+static PyObject *
+infpy_threads (PyObject *self, PyObject *args)
+{
+  int i;
+  struct threadlist_entry *entry;
+  inferior_object *inf_obj = (inferior_object *) self;
+  PyObject *tuple;
+
+  INFPY_REQUIRE_VALID (inf_obj);
+
+  tuple = PyTuple_New (inf_obj->nthreads);
+  if (!tuple)
+    return NULL;
+
+  /* The list is in reverse order of thread age (i.e., newest comes
+     first).  */
+  for (i = 0, entry = inf_obj->threads; i < inf_obj->nthreads;
+       i++, entry = entry->next)
+    {
+      Py_INCREF (entry->thread_obj);
+      PyTuple_SET_ITEM (tuple, i, (PyObject *) entry->thread_obj);
+    }
+
+  return tuple;
+}
+
+static PyObject *
+infpy_get_num (PyObject *self, void *closure)
+{
+  inferior_object *inf = (inferior_object *) self;
+
+  INFPY_REQUIRE_VALID (inf);
+
+  return PyLong_FromLong (inf->inferior->num);
+}
+
+static PyObject *
+infpy_get_pid (PyObject *self, void *closure)
+{
+  inferior_object *inf = (inferior_object *) self;
+
+  INFPY_REQUIRE_VALID (inf);
+
+  return PyLong_FromLong (inf->inferior->pid);
+}
+
+static PyObject *
+infpy_get_was_attached (PyObject *self, void *closure)
+{
+  inferior_object *inf = (inferior_object *) self;
+  INFPY_REQUIRE_VALID (inf);
+  if (inf->inferior->attach_flag)
+    Py_RETURN_TRUE;
+  Py_RETURN_FALSE;
+}
+
+\f
+
+static int
+build_inferior_list (struct inferior *inf, void *arg)
+{
+  PyObject *list = arg;
+  PyObject *inferior = inferior_to_inferior_object (inf);
+  PyList_Append (list, inferior);
+
+  return 0;
+}
+
+/* Implementation of gdb.inferiors () -> (gdb.Inferior, ...).
+   Returns a tuple of all inferiors.  */
+PyObject *
+gdbpy_inferiors (PyObject *unused, PyObject *unused2)
+{
+  int i = 0;
+  PyObject *list, *inferior;
+  struct inferior *inf;
+
+  list = PyList_New (0);
+  if (!list)
+    return NULL;
+
+  iterate_over_inferiors (build_inferior_list, list);
+
+  return PyList_AsTuple (list);
+}
+
+\f
+
+/* Membuf and memory manipulation.  */
+
+/* Implementation of gdb.read_memory (address, length).
+   Returns a Python buffer object with LENGTH bytes of the inferior's
+   memory at ADDRESS.  Both arguments are integers.  */
+static PyObject *
+infpy_read_memory (PyObject *self, PyObject *args)
+{
+  int error = 0;
+  CORE_ADDR addr, length;
+  void *buffer = NULL;
+  membuf_object *membuf_obj;
+  PyObject *addr_obj, *length_obj;
+  struct cleanup *cleanups;
+  volatile struct gdb_exception except;
+
+  if (! PyArg_ParseTuple (args, "OO", &addr_obj, &length_obj))
+    return NULL;
+
+  cleanups = make_cleanup (null_cleanup, NULL);
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      if (!get_addr_from_python (addr_obj, &addr)
+	  || !get_addr_from_python (length_obj, &length))
+	{
+	  error = 1;
+	  break;
+	}
+
+      buffer = xmalloc (length);
+      make_cleanup (xfree, buffer);
+
+      read_memory (addr, buffer, length);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  if (error)
+    {
+      do_cleanups (cleanups);
+      return NULL;
+    }
+
+  membuf_obj = PyObject_New (membuf_object, &membuf_object_type);
+  if (membuf_obj == NULL)
+    {
+      PyErr_SetString (PyExc_MemoryError,
+		       _("Could not allocate memory buffer object."));
+      do_cleanups (cleanups);
+      return NULL;
+    }
+
+  discard_cleanups (cleanups);
+
+  membuf_obj->buffer = buffer;
+  membuf_obj->addr = addr;
+  membuf_obj->length = length;
+
+  return PyBuffer_FromReadWriteObject ((PyObject *) membuf_obj, 0,
+				       Py_END_OF_BUFFER);
+}
+
+/* Implementation of gdb.write_memory (address, buffer [, length]).
+   Writes the contents of BUFFER (a Python object supporting the read
+   buffer protocol) at ADDRESS in the inferior's memory.  Write LENGTH
+   bytes from BUFFER, or its entire contents if the argument is not
+   provided.  The function returns nothing.  */
+static PyObject *
+infpy_write_memory (PyObject *self, PyObject *args)
+{
+  int buf_len, error = 0;
+  const char *buffer;
+  CORE_ADDR addr, length;
+  PyObject *addr_obj, *length_obj = NULL;
+  volatile struct gdb_exception except;
+
+  if (! PyArg_ParseTuple (args, "Os#|O", &addr_obj, &buffer, &buf_len,
+			  &length_obj))
+    return NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      if (!get_addr_from_python (addr_obj, &addr))
+	{
+	  error = 1;
+	  break;
+	}
+
+      if (!length_obj)
+	length = buf_len;
+      else if (!get_addr_from_python (length_obj, &length))
+	{
+	  error = 1;
+	  break;
+	}
+
+      write_memory (addr, buffer, length);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  if (error)
+    return NULL;
+
+  Py_RETURN_NONE;
+}
+
+/* Destructor of Membuf objects.  */
+static void
+mbpy_dealloc (PyObject *self)
+{
+  xfree (((membuf_object *) self)->buffer);
+  self->ob_type->tp_free (self);
+}
+
+/* Return a description of the Membuf object.  */
+static PyObject *
+mbpy_str (PyObject *self)
+{
+  membuf_object *membuf_obj = (membuf_object *) self;
+
+  return PyString_FromFormat (_("Memory buffer for address %s, \
+which is %s bytes long."),
+			      paddress (python_gdbarch, membuf_obj->addr),
+			      pulongest (membuf_obj->length));
+}
+
+static Py_ssize_t
+get_read_buffer (PyObject *self, Py_ssize_t segment, void **ptrptr)
+{
+  membuf_object *membuf_obj = (membuf_object *) self;
+
+  if (segment)
+    {
+      PyErr_SetString (PyExc_SystemError,
+		       _("The memory buffer supports only one segment."));
+      return -1;
+    }
+
+  *ptrptr = membuf_obj->buffer;
+
+  return membuf_obj->length;
+}
+
+static Py_ssize_t
+get_write_buffer (PyObject *self, Py_ssize_t segment, void **ptrptr)
+{
+  return get_read_buffer (self, segment, ptrptr);
+}
+
+static Py_ssize_t
+get_seg_count (PyObject *self, Py_ssize_t *lenp)
+{
+  if (lenp)
+    *lenp = ((membuf_object *) self)->length;
+
+  return 1;
+}
+
+static Py_ssize_t
+get_char_buffer (PyObject *self, Py_ssize_t segment, char **ptrptr)
+{
+  void *ptr = NULL;
+  Py_ssize_t ret;
+
+  ret = get_read_buffer (self, segment, &ptr);
+  *ptrptr = (char *) ptr;
+
+  return ret;
+}
+
+/* Implementation of
+   gdb.search_memory (address, length, pattern).  ADDRESS is the
+   address to start the search.  LENGTH specifies the scope of the
+   search from ADDRESS.  PATTERN is the pattern to search for (and
+   must be a Python object supporting the buffer protocol).
+   Returns a Python Long object holding the address where the pattern
+   was located, or if the pattern was not found, returns None.  */
+static PyObject *
+infpy_search_memory (PyObject *self, PyObject *args, PyObject *kw)
+{
+  CORE_ADDR start_addr, length;
+  static char *keywords[] = { "address", "length", "pattern", NULL };
+  PyObject *pattern, *start_addr_obj, *length_obj;
+  volatile struct gdb_exception except;
+  Py_ssize_t pattern_size;
+  const void *buffer;
+  CORE_ADDR found_addr;
+  int found = 0;
+
+  if (! PyArg_ParseTupleAndKeywords (args, kw, "OOO", keywords,
+				     &start_addr_obj, &length_obj,
+				     &pattern))
+    return NULL;
+
+  if (get_addr_from_python (start_addr_obj, &start_addr)
+      && get_addr_from_python (length_obj, &length))
+    {
+      if (!length)
+	{
+	  PyErr_SetString (PyExc_ValueError,
+			   _("Search range is empty."));
+	  return NULL;
+	}
+      /* Watch for overflows.  */
+      else if (length > CORE_ADDR_MAX
+	       || (start_addr + length - 1) < start_addr)
+	{
+	  PyErr_SetString (PyExc_ValueError,
+			   _("The search range is too large."));
+
+	  return NULL;
+	}
+    }
+  else
+    {
+      PyErr_SetString (PyExc_RuntimeError,
+		       _("Cannot get search address/range from Python."));
+
+      return NULL;
+    }
+
+  if (!PyObject_CheckReadBuffer (pattern))
+    {
+      PyErr_SetString (PyExc_RuntimeError,
+		       _("The pattern is not a Python buffer."));
+
+      return NULL;
+    }
+
+  if (PyObject_AsReadBuffer (pattern, &buffer, &pattern_size) == -1)
+    return NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      found = target_search_memory (start_addr, length,
+				    buffer, pattern_size,
+				    &found_addr);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  if (found)
+    return PyLong_FromLong (found_addr);
+  else
+    Py_RETURN_NONE;
+}
+
+\f
+
+/* Clear the INFERIOR pointer in an Inferior object and clear the
+   thread list.  */
+static void
+py_free_inferior (struct inferior *inf, void *datum)
+{
+
+  struct cleanup *cleanup;
+  inferior_object *inf_obj = datum;
+  struct threadlist_entry *th_entry, *th_tmp;
+
+  cleanup = ensure_python_env (python_gdbarch, python_language);
+
+  inf_obj->inferior = NULL;
+
+  /* Deallocate threads list.  */
+  for (th_entry = inf_obj->threads; th_entry != NULL;)
+    {
+      Py_DECREF (th_entry->thread_obj);
+
+      th_tmp = th_entry;
+      th_entry = th_entry->next;
+      xfree (th_tmp);
+    }
+
+  inf_obj->nthreads = 0;
+
+  Py_DECREF ((PyObject *) inf_obj);
+  do_cleanups (cleanup);
+}
+
+void
+gdbpy_initialize_inferior (void)
+{
+  if (PyType_Ready (&inferior_object_type) < 0)
+    return;
+
+  Py_INCREF (&inferior_object_type);
+  PyModule_AddObject (gdb_module, "Inferior",
+		      (PyObject *) &inferior_object_type);
+
+  infpy_inf_data_key =
+    register_inferior_data_with_cleanup (py_free_inferior);
+
+  observer_attach_new_thread (add_thread_object);
+  observer_attach_thread_exit (delete_thread_object);
+
+  if (PyType_Ready (&membuf_object_type) < 0)
+    return;
+
+  Py_INCREF (&membuf_object_type);
+  PyModule_AddObject (gdb_module, "Membuf", (PyObject *)
+		      &membuf_object_type);
+}
+
+\f
+
+static PyGetSetDef inferior_object_getset[] =
+{
+  { "num", infpy_get_num, NULL, "ID of inferior, as assigned by GDB.", NULL },
+  { "pid", infpy_get_pid, NULL, "PID of inferior, as assigned by the OS.",
+    NULL },
+  { "was_attached", infpy_get_was_attached, NULL,
+    "True if the inferior was created using 'attach'.", NULL },
+  { NULL }
+};
+
+static PyMethodDef inferior_object_methods[] =
+{
+  { "threads", infpy_threads, METH_NOARGS,
+    "Return all the threads of this inferior." },
+  { "read_memory", infpy_read_memory, METH_VARARGS,
+    "read_memory (address, length) -> buffer\n\
+Return a buffer object for reading from the inferior's memory." },
+  { "write_memory", infpy_write_memory, METH_VARARGS,
+    "write_memory (address, buffer [, length])\n\
+Write the given buffer object to the inferior's memory." },
+  { "search_memory", (PyCFunction) infpy_search_memory,
+    METH_VARARGS | METH_KEYWORDS,
+    "search_memory (address, length, pattern) -> long\n\
+Return a long with the address of a match, or None." },
+
+  { NULL }
+};
+
+static PyTypeObject inferior_object_type =
+{
+  PyObject_HEAD_INIT (NULL)
+  0,				  /* ob_size */
+  "gdb.Inferior",		  /* tp_name */
+  sizeof (inferior_object),	  /* tp_basicsize */
+  0,				  /* tp_itemsize */
+  0,				  /* tp_dealloc */
+  0,				  /* tp_print */
+  0,				  /* tp_getattr */
+  0,				  /* tp_setattr */
+  0,				  /* tp_compare */
+  0,				  /* tp_repr */
+  0,				  /* tp_as_number */
+  0,				  /* tp_as_sequence */
+  0,				  /* tp_as_mapping */
+  0,				  /* tp_hash  */
+  0,				  /* tp_call */
+  0,				  /* tp_str */
+  0,				  /* tp_getattro */
+  0,				  /* tp_setattro */
+  0,				  /* tp_as_buffer */
+  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER,  /* tp_flags */
+  "GDB inferior object",	  /* tp_doc */
+  0,				  /* tp_traverse */
+  0,				  /* tp_clear */
+  0,				  /* tp_richcompare */
+  0,				  /* tp_weaklistoffset */
+  0,				  /* tp_iter */
+  0,				  /* tp_iternext */
+  inferior_object_methods,	  /* tp_methods */
+  0,				  /* tp_members */
+  inferior_object_getset,	  /* tp_getset */
+  0,				  /* tp_base */
+  0,				  /* tp_dict */
+  0,				  /* tp_descr_get */
+  0,				  /* tp_descr_set */
+  0,				  /* tp_dictoffset */
+  0,				  /* tp_init */
+  0				  /* tp_alloc */
+};
+
+\f
+
+/* Python doesn't provide a decent way to get compatibility here.  */
+#if HAVE_LIBPYTHON2_4
+#define CHARBUFFERPROC_NAME getcharbufferproc
+#else
+#define CHARBUFFERPROC_NAME charbufferproc
+#endif
+
+static PyBufferProcs buffer_procs = {
+  get_read_buffer,
+  get_write_buffer,
+  get_seg_count,
+  /* The cast here works around a difference between Python 2.4 and
+     Python 2.5.  */
+  (CHARBUFFERPROC_NAME) get_char_buffer
+};
+
+static PyTypeObject membuf_object_type = {
+  PyObject_HEAD_INIT (NULL)
+  0,				  /*ob_size*/
+  "gdb.Membuf",			  /*tp_name*/
+  sizeof (membuf_object),	  /*tp_basicsize*/
+  0,				  /*tp_itemsize*/
+  mbpy_dealloc,			  /*tp_dealloc*/
+  0,				  /*tp_print*/
+  0,				  /*tp_getattr*/
+  0,				  /*tp_setattr*/
+  0,				  /*tp_compare*/
+  0,				  /*tp_repr*/
+  0,				  /*tp_as_number*/
+  0,				  /*tp_as_sequence*/
+  0,				  /*tp_as_mapping*/
+  0,				  /*tp_hash */
+  0,				  /*tp_call*/
+  mbpy_str,			  /*tp_str*/
+  0,				  /*tp_getattro*/
+  0,				  /*tp_setattro*/
+  &buffer_procs,		  /*tp_as_buffer*/
+  Py_TPFLAGS_DEFAULT,		  /*tp_flags*/
+  "GDB memory buffer object", 	  /*tp_doc*/
+  0,				  /* tp_traverse */
+  0,				  /* tp_clear */
+  0,				  /* tp_richcompare */
+  0,				  /* tp_weaklistoffset */
+  0,				  /* tp_iter */
+  0,				  /* tp_iternext */
+  0,				  /* tp_methods */
+  0,				  /* tp_members */
+  0,				  /* tp_getset */
+  0,				  /* tp_base */
+  0,				  /* tp_dict */
+  0,				  /* tp_descr_get */
+  0,				  /* tp_descr_set */
+  0,				  /* tp_dictoffset */
+  0,				  /* tp_init */
+  0,				  /* tp_alloc */
+  PyType_GenericNew		  /* tp_new */
+};
diff --git a/gdb/python/py-infthread.c b/gdb/python/py-infthread.c
new file mode 100644
index 0000000..dfeb285
--- /dev/null
+++ b/gdb/python/py-infthread.c
@@ -0,0 +1,303 @@
+/* Python interface to inferior threads.
+
+   Copyright (C) 2009, 2010 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 "exceptions.h"
+#include "gdbthread.h"
+#include "inferior.h"
+#include "python-internal.h"
+
+static PyTypeObject thread_object_type;
+
+/* Require that INFERIOR be a valid inferior ID.  */
+#define THPY_REQUIRE_VALID(Thread)				\
+  do {								\
+    if (!Thread->thread)					\
+      {								\
+	PyErr_SetString (PyExc_RuntimeError,			\
+			 _("Thread no longer exists."));	\
+	return NULL;						\
+      }								\
+  } while (0)
+
+\f
+
+thread_object *
+create_thread_object (struct thread_info *tp)
+{
+  thread_object *thread_obj;
+
+  thread_obj = PyObject_New (thread_object, &thread_object_type);
+  if (!thread_obj)
+    return NULL;
+
+  thread_obj->thread = tp;
+  thread_obj->inf_obj = find_inferior_object (PIDGET (tp->ptid));
+  Py_INCREF (thread_obj->inf_obj);
+
+  return thread_obj;
+}
+
+\f
+
+static void
+thpy_dealloc (PyObject *self)
+{
+  Py_DECREF (((thread_object *) self)->inf_obj);
+  self->ob_type->tp_free (self);
+}
+
+static PyObject *
+thpy_get_num (PyObject *self, void *closure)
+{
+  thread_object *thread_obj = (thread_object *) self;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  return PyLong_FromLong (thread_obj->thread->num);
+}
+
+/* Getter for InferiorThread.ptid  -> (pid, lwp, tid).
+   Returns a tuple with the thread's ptid components.  */
+static PyObject *
+thpy_get_ptid (PyObject *self, void *closure)
+{
+  int pid;
+  long tid, lwp;
+  thread_object *thread_obj = (thread_object *) self;
+  PyObject *ret;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  ret = PyTuple_New (3);
+  if (!ret)
+    return NULL;
+
+  pid = ptid_get_pid (thread_obj->thread->ptid);
+  lwp = ptid_get_lwp (thread_obj->thread->ptid);
+  tid = ptid_get_tid (thread_obj->thread->ptid);
+
+  PyTuple_SET_ITEM (ret, 0, PyInt_FromLong (pid));
+  PyTuple_SET_ITEM (ret, 1, PyInt_FromLong (lwp));
+  PyTuple_SET_ITEM (ret, 2, PyInt_FromLong (tid));
+
+  return ret;
+}
+
+\f
+
+/* Implementation of InferiorThread.newest_frame () -> gdb.Frame.
+   Returns the newest frame object.  */
+PyObject *
+thpy_newest_frame (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame;
+  PyObject *frame_obj = NULL;   /* Initialize to appease gcc warning.  */
+  thread_object *thread_obj = (thread_object *) self;
+  struct cleanup *cleanup;
+  volatile struct gdb_exception except;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  cleanup = make_cleanup_restore_current_thread ();
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      switch_to_thread (thread_obj->thread->ptid);
+
+      frame = get_current_frame ();
+      frame_obj = frame_info_to_frame_object (frame);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  do_cleanups (cleanup);
+
+  return frame_obj;
+}
+
+/* Implementation of InferiorThread.switch ().
+   Makes this the GDB selected thread.  */
+static PyObject *
+thpy_switch (PyObject *self, PyObject *args)
+{
+  thread_object *thread_obj = (thread_object *) self;
+  struct cleanup *cleanup;
+  volatile struct gdb_exception except;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      switch_to_thread (thread_obj->thread->ptid);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  Py_RETURN_NONE;
+}
+
+/* Implementation of InferiorThread.is_stopped () -> Boolean.
+   Return whether the thread is stopped.  */
+static PyObject *
+thpy_is_stopped (PyObject *self, PyObject *args)
+{
+  thread_object *thread_obj = (thread_object *) self;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  if (is_stopped (thread_obj->thread->ptid))
+    Py_RETURN_TRUE;
+
+  Py_RETURN_FALSE;
+}
+
+/* Implementation of InferiorThread.is_running () -> Boolean.
+   Return whether the thread is running.  */
+static PyObject *
+thpy_is_running (PyObject *self, PyObject *args)
+{
+  thread_object *thread_obj = (thread_object *) self;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  if (is_running (thread_obj->thread->ptid))
+    Py_RETURN_TRUE;
+
+  Py_RETURN_FALSE;
+}
+
+/* Implementation of InferiorThread.is_exited () -> Boolean.
+   Return whether the thread is exited.  */
+static PyObject *
+thpy_is_exited (PyObject *self, PyObject *args)
+{
+  thread_object *thread_obj = (thread_object *) self;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  if (is_exited (thread_obj->thread->ptid))
+    Py_RETURN_TRUE;
+
+  Py_RETURN_FALSE;
+}
+
+\f
+
+/* Implementation of gdb.selected_thread () -> gdb.InferiorThread.
+   Returns the selected thread object.  */
+PyObject *
+gdbpy_selected_thread (PyObject *self, PyObject *args)
+{
+  PyObject *thread_obj;
+
+  thread_obj = (PyObject *) find_thread_object (inferior_ptid);
+  if (thread_obj)
+    {
+      Py_INCREF (thread_obj);
+      return thread_obj;
+    }
+
+  Py_RETURN_NONE;
+}
+
+\f
+
+void
+gdbpy_initialize_thread (void)
+{
+  if (PyType_Ready (&thread_object_type) < 0)
+    return;
+
+  Py_INCREF (&thread_object_type);
+  PyModule_AddObject (gdb_module, "InferiorThread",
+		      (PyObject *) &thread_object_type);
+}
+
+\f
+
+static PyGetSetDef thread_object_getset[] =
+{
+  { "num", thpy_get_num, NULL, "ID of the thread, as assigned by GDB.", NULL },
+  { "ptid", thpy_get_ptid, NULL, "ID of the thread, as assigned by the OS.",
+    NULL },
+
+  { NULL }
+};
+
+static PyMethodDef thread_object_methods[] =
+{
+  { "newest_frame", thpy_newest_frame, METH_NOARGS,
+    "newest_frame () -> gdb.Frame\n\
+Return the newest frame in the thread." },
+  { "switch", thpy_switch, METH_NOARGS,
+    "switch ()\n\
+Makes this the GDB selected thread." },
+  { "is_stopped", thpy_is_stopped, METH_NOARGS,
+    "is_stopped () -> Boolean\n\
+Return whether the thread is stopped." },
+  { "is_running", thpy_is_running, METH_NOARGS,
+    "is_running () -> Boolean\n\
+Return whether the thread is running." },
+  { "is_exited", thpy_is_exited, METH_NOARGS,
+    "is_exited () -> Boolean\n\
+Return whether the thread is exited." },
+
+  { NULL }
+};
+
+static PyTypeObject thread_object_type =
+{
+  PyObject_HEAD_INIT (NULL)
+  0,				  /*ob_size*/
+  "gdb.InferiorThread",		  /*tp_name*/
+  sizeof (thread_object),	  /*tp_basicsize*/
+  0,				  /*tp_itemsize*/
+  thpy_dealloc,			  /*tp_dealloc*/
+  0,				  /*tp_print*/
+  0,				  /*tp_getattr*/
+  0,				  /*tp_setattr*/
+  0,				  /*tp_compare*/
+  0,				  /*tp_repr*/
+  0,				  /*tp_as_number*/
+  0,				  /*tp_as_sequence*/
+  0,				  /*tp_as_mapping*/
+  0,				  /*tp_hash */
+  0,				  /*tp_call*/
+  0,				  /*tp_str*/
+  0,				  /*tp_getattro*/
+  0,				  /*tp_setattro*/
+  0,				  /*tp_as_buffer*/
+  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER,  /*tp_flags*/
+  "GDB thread object",		  /* tp_doc */
+  0,				  /* tp_traverse */
+  0,				  /* tp_clear */
+  0,				  /* tp_richcompare */
+  0,				  /* tp_weaklistoffset */
+  0,				  /* tp_iter */
+  0,				  /* tp_iternext */
+  thread_object_methods,	  /* tp_methods */
+  0,				  /* tp_members */
+  thread_object_getset,		  /* tp_getset */
+  0,				  /* tp_base */
+  0,				  /* tp_dict */
+  0,				  /* tp_descr_get */
+  0,				  /* tp_descr_set */
+  0,				  /* tp_dictoffset */
+  0,				  /* tp_init */
+  0				  /* tp_alloc */
+};
diff --git a/gdb/python/py-utils.c b/gdb/python/py-utils.c
index 944e6b4..90609fa 100644
--- a/gdb/python/py-utils.c
+++ b/gdb/python/py-utils.c
@@ -19,6 +19,7 @@
 
 #include "defs.h"
 #include "charset.h"
+#include "value.h"
 #include "python-internal.h"
 
 
@@ -272,3 +273,51 @@ gdbpy_exception_to_string (PyObject *ptype, PyObject *pvalue)
 
   return str;
 }
+
+/* Converts OBJ to a CORE_ADDR value.
+
+   Returns 1 on success or 0 on failure, with a Python exception set.  This
+   function can also throw GDB exceptions.
+*/
+
+int
+get_addr_from_python (PyObject *obj, CORE_ADDR *addr)
+{
+  if (gdbpy_is_value_object (obj))
+    *addr = value_as_address (value_object_to_value (obj));
+  else if (PyLong_Check (obj))
+    {
+      /* Assume CORE_ADDR corresponds to unsigned long.  */
+      *addr = PyLong_AsUnsignedLong (obj);
+      if (PyErr_Occurred () != NULL)
+	return 0;
+    }
+  else if (PyInt_Check (obj))
+    {
+      long val;
+
+      /* Assume CORE_ADDR corresponds to unsigned long.  */
+      val = PyInt_AsLong (obj);
+
+      if (val >= 0)
+	*addr = val;
+      else
+      {
+	/* If no error ocurred, VAL is indeed negative.  */
+	if (PyErr_Occurred () != NULL)
+	  return 0;
+
+	PyErr_SetString (PyExc_ValueError,
+			 _("Supplied address is negative."));
+	return 0;
+      }
+    }
+  else
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       _("Invalid type for address."));
+      return 0;
+    }
+
+  return 1;
+}
diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c
index eb20de8..2024021 100644
--- a/gdb/python/py-value.c
+++ b/gdb/python/py-value.c
@@ -43,6 +43,10 @@
 /* Python's long type corresponds to C's long long type.  */
 #define builtin_type_pylong builtin_type (python_gdbarch)->builtin_long_long
 
+/* Python's long type corresponds to C's long long type.  Unsigned version.  */
+#define builtin_type_upylong builtin_type \
+  (python_gdbarch)->builtin_unsigned_long_long
+
 #define builtin_type_pybool \
   language_bool_type (python_language, python_gdbarch)
 
@@ -961,7 +965,34 @@ convert_value_from_python (PyObject *obj)
 	{
 	  LONGEST l = PyLong_AsLongLong (obj);
 
-	  if (! PyErr_Occurred ())
+	  if (PyErr_Occurred ())
+	    {
+	      /* If the error was an overflow, we can try converting to
+	         ULONGEST instead.  */
+	      if (PyErr_ExceptionMatches (PyExc_OverflowError))
+		{
+		  PyObject *etype, *evalue, *etraceback, *zero;
+
+		  PyErr_Fetch (&etype, &evalue, &etraceback);
+		  zero = PyInt_FromLong (0);
+
+		  /* Check whether obj is positive.  */
+		  if (PyObject_RichCompareBool (obj, zero, Py_GT) > 0)
+		    {
+		      ULONGEST ul;
+
+		      ul = PyLong_AsUnsignedLongLong (obj);
+		      if (! PyErr_Occurred ())
+			value = value_from_ulongest (builtin_type_upylong, ul);
+		    }
+		  else
+		    /* There's nothing we can do.  */
+		    PyErr_Restore (etype, evalue, etraceback);
+
+		  Py_DECREF (zero);
+		}
+	    }
+	  else
 	    value = value_from_longest (builtin_type_pylong, l);
 	}
       else if (PyFloat_Check (obj))
@@ -1028,6 +1059,14 @@ gdbpy_history (PyObject *self, PyObject *args)
   return value_to_value_object (res_val);
 }
 
+/* Returns 1 in OBJ is a gdb.Value object, 0 otherwise.  */
+
+int
+gdbpy_is_value_object (PyObject *obj)
+{
+  return PyObject_TypeCheck (obj, &value_object_type);
+}
+
 void
 gdbpy_initialize_values (void)
 {
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 5bd2a4b..daaed2c 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -80,6 +80,17 @@ extern PyTypeObject value_object_type;
 extern PyTypeObject block_object_type;
 extern PyTypeObject symbol_object_type;
 
+typedef struct
+{
+  PyObject_HEAD
+
+  /* The thread we represent.  */
+  struct thread_info *thread;
+
+  /* The Inferior object to which this thread belongs.  */
+  PyObject *inf_obj;
+} thread_object;
+
 extern struct cmd_list_element *set_python_list;
 extern struct cmd_list_element *show_python_list;
 
@@ -92,6 +103,8 @@ PyObject *gdbpy_block_for_pc (PyObject *self, PyObject *args);
 PyObject *gdbpy_lookup_type (PyObject *self, PyObject *args, PyObject *kw);
 PyObject *gdbpy_create_lazy_string_object (CORE_ADDR address, long length,
 					   const char *encoding, struct type *type);
+PyObject *gdbpy_inferiors (PyObject *unused, PyObject *unused2);
+PyObject *gdbpy_selected_thread (PyObject *self, PyObject *args);
 PyObject *gdbpy_string_to_argv (PyObject *self, PyObject *args);
 PyObject *gdbpy_get_hook_function (const char *);
 PyObject *gdbpy_parameter (PyObject *self, PyObject *args);
@@ -106,6 +119,7 @@ PyObject *symbol_to_symbol_object (struct symbol *sym);
 PyObject *block_to_block_object (struct block *block, struct objfile *objfile);
 PyObject *value_to_value_object (struct value *v);
 PyObject *type_to_type_object (struct type *);
+PyObject *frame_info_to_frame_object (struct frame_info *frame);
 
 PyObject *pspace_to_pspace_object (struct program_space *);
 PyObject *pspy_get_printers (PyObject *, void *);
@@ -113,6 +127,10 @@ PyObject *pspy_get_printers (PyObject *, void *);
 PyObject *objfile_to_objfile_object (struct objfile *);
 PyObject *objfpy_get_printers (PyObject *, void *);
 
+thread_object *create_thread_object (struct thread_info *tp);
+thread_object *find_thread_object (ptid_t ptid);
+PyObject *find_inferior_object (int pid);
+
 struct block *block_object_to_block (PyObject *obj);
 struct symbol *symbol_object_to_symbol (PyObject *obj);
 struct value *value_object_to_value (PyObject *self);
@@ -136,6 +154,8 @@ void gdbpy_initialize_objfile (void);
 void gdbpy_initialize_breakpoints (void);
 void gdbpy_initialize_lazy_string (void);
 void gdbpy_initialize_parameters (void);
+void gdbpy_initialize_thread (void);
+void gdbpy_initialize_inferior (void);
 
 struct cleanup *make_cleanup_py_decref (PyObject *py);
 
@@ -188,6 +208,8 @@ gdb_byte *gdbpy_extract_lazy_string (PyObject *string,
 				     struct type **str_type, 
 				     long *length, char **encoding);
 
+int gdbpy_is_value_object (PyObject *obj);
+
 /* Note that these are declared here, and not in python.h with the
    other pretty-printer functions, because they refer to PyObject.  */
 PyObject *apply_varobj_pretty_printer (PyObject *print_obj,
@@ -204,4 +226,6 @@ extern PyObject *gdbpy_enabled_cst;
 
 extern PyObject *gdbpy_gdberror_exc;
 
+int get_addr_from_python (PyObject *obj, CORE_ADDR *addr);
+
 #endif /* GDB_PYTHON_INTERNAL_H */
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 31880c1..77aed6e 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -686,6 +686,8 @@ Enables or disables printing of Python stack traces."),
   gdbpy_initialize_objfile ();
   gdbpy_initialize_breakpoints ();
   gdbpy_initialize_lazy_string ();
+  gdbpy_initialize_thread ();
+  gdbpy_initialize_inferior ();
 
   PyRun_SimpleString ("import gdb");
   PyRun_SimpleString ("gdb.pretty_printers = []");
@@ -799,7 +801,12 @@ Arguments are separate by spaces and may be quoted."
     "Write a string using gdb's filtered stream." },
   { "flush", gdbpy_flush, METH_NOARGS,
     "Flush gdb's filtered stdout stream." },
-
+  { "selected_thread", gdbpy_selected_thread, METH_NOARGS,
+    "selected_thread () -> gdb.InferiorThread.\n\
+Return the selected thread object." },
+  { "inferiors", gdbpy_inferiors, METH_NOARGS,
+    "inferiors () -> (gdb.Inferior, ...).\n\
+Return a tuple containing all inferiors." },
   {NULL, NULL, 0, NULL}
 };
 
diff --git a/gdb/testsuite/gdb.python/Makefile.in b/gdb/testsuite/gdb.python/Makefile.in
index c6a2678..660941d 100644
--- a/gdb/testsuite/gdb.python/Makefile.in
+++ b/gdb/testsuite/gdb.python/Makefile.in
@@ -2,7 +2,7 @@ VPATH = @srcdir@
 srcdir = @srcdir@
 
 EXECUTABLES = py-type py-value py-prettyprint py-template py-block \
-		py-symbol py-mi py-breakpoint
+		py-symbol py-mi py-breakpoint py-inferior py-infthread
 
 all info install-info dvi install uninstall installcheck check:
 	@echo "Nothing to be done for $@..."
diff --git a/gdb/testsuite/gdb.python/py-inferior.c b/gdb/testsuite/gdb.python/py-inferior.c
new file mode 100644
index 0000000..0b48299
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-inferior.c
@@ -0,0 +1,49 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.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;
+
+
+int f2 (int a)
+{
+  char *str = "hello, testsuite";
+
+  puts (str);	/* Break here.  */
+
+  return ++a;
+}
+
+int f1 (int a, int b)
+{
+  return f2(a) + b;
+}
+
+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 (int argc, char *argv[])
+{
+  init_bufs ();
+
+  return f1 (1, 2);
+}
diff --git a/gdb/testsuite/gdb.python/py-inferior.exp b/gdb/testsuite/gdb.python/py-inferior.exp
new file mode 100644
index 0000000..309ae8c
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-inferior.exp
@@ -0,0 +1,191 @@
+# Copyright (C) 2009, 2010 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 file is part of the GDB testsuite.  It tests the mechanism
+# exposing inferiors to Python.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+# Run a command in GDB, and report a failure if a Python exception is thrown.
+# If report_pass is true, report a pass if no exception is thrown.
+proc gdb_py_test_silent_cmd {cmd name report_pass} {
+    global gdb_prompt
+
+  gdb_test_multiple $cmd $name {
+      -re "Traceback.*$gdb_prompt $"  { fail $name }
+      -re "$gdb_prompt $"	      { if $report_pass { pass $name } }
+  }
+}
+
+set testfile "py-inferior"
+set srcfile ${testfile}.c
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
+    return -1
+}
+
+# Start with a fresh gdb.
+clean_restart ${testfile}
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+# The following tests require execution.
+
+if ![runto_main] then {
+    fail "Can't run to main"
+    return 0
+}
+
+runto [gdb_get_line_number "Break here."]
+
+# Test basic gdb.Inferior attributes and methods.
+
+gdb_py_test_silent_cmd "python inferiors = gdb.inferiors ()" "get inferiors list" 1
+gdb_test "python print inferiors" "\\(<gdb.Inferior object at 0x\[\[:xdigit:\]\]+>,\\)" "verify inferiors list"
+gdb_py_test_silent_cmd "python i0 = inferiors\[0\]" "get first inferior" 0
+
+gdb_test "python print 'result =', i0 == inferiors\[0\]" " = True" "test equality comparison (true)"
+gdb_test "python print 'result =', i0.num" " = \[0-9\]+" "test Inferior.num"
+gdb_test "python print 'result =', i0.pid" " = \[0-9\]+" "test Inferior.pid"
+gdb_test "python print 'result =', i0.was_attached" " = False" "test Inferior.was_attached"
+gdb_test "python print i0.threads ()" "\\(<gdb.InferiorThread object at 0x\[\[:xdigit:\]\]+>,\\)" "test Inferior.threads"
+
+# Test memory read and write operations.
+
+gdb_py_test_silent_cmd "python addr = gdb.selected_frame ().read_var ('str')" \
+  "read str address" 0
+gdb_py_test_silent_cmd "python str = gdb.inferiors()\[0\].read_memory (addr, 5)" \
+  "read str contents" 1
+gdb_py_test_silent_cmd "python str\[1\] = 'a'" "change str" 0
+gdb_py_test_silent_cmd "python gdb.inferiors()\[0\].write_memory (addr, str)" \
+  "write str" 1
+gdb_test "print str" " = 0x\[\[:xdigit:\]\]+ \"hallo, testsuite\"" \
+  "ensure str was changed in the inferior"
+
+# Test memory search.
+
+set hex_number {0x[0-9a-fA-F][0-9a-fA-F]*}
+set dec_number {[0-9]+}
+set history_prefix {[$][0-9]* = }
+set newline {[\r\n]+}
+set pattern_not_found "${newline}.None"
+set one_pattern_found "${newline}.${dec_number}"
+
+# Test string pattern.
+
+gdb_test "set *(int32_t*) &int8_search_buf\[10\] = 0x61616161" "" ""
+gdb_test "py search_buf = gdb.selected_frame ().read_var ('int8_search_buf')" "" ""
+gdb_test "py start_addr = search_buf.address" "" ""
+gdb_test "py length = search_buf.type.sizeof" "" ""
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, 'aaa')" \
+  "${one_pattern_found}" "find string pattern"
+
+# Test not finding pattern because search range too small, with
+# potential find at the edge of the range.
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, 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 "py print gdb.inferiors()\[0\].search_memory (start_addr, 10+3+1, 'aaa')" \
+  "${one_pattern_found}" "pattern found at end of range"
+
+# Import struct to pack the following patterns.
+gdb_test "py from struct import *" "" ""
+
+# Test 16-bit pattern.
+
+gdb_test "set int16_search_buf\[10\] = 0x1234" "" ""
+gdb_test "py search_buf = gdb.selected_frame ().read_var ('int16_search_buf')" "" ""
+gdb_test "py start_addr = search_buf.address" "" ""
+gdb_test "py length = search_buf.type.sizeof" "" ""
+gdb_test "py pattern = pack('H',0x1234)" "" \
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, pattern)" \
+  "${one_pattern_found}" "find 16-bit pattern, with value pattern"
+
+# Test 32-bit pattern.
+
+gdb_test "set int32_search_buf\[10\] = 0x12345678" "" ""
+gdb_test "py search_buf = gdb.selected_frame ().read_var ('int32_search_buf')" "" ""
+gdb_test "py start_addr = search_buf.address" "" ""
+gdb_test "py length = search_buf.type.sizeof" "" ""
+gdb_test "py pattern = pack('I',0x12345678)" "" \
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, pattern)" \
+  "${one_pattern_found}" "find 32-bit pattern, with python pattern"
+
+# Test 64-bit pattern.
+
+gdb_test "set int64_search_buf\[10\] = 0xfedcba9876543210LL" "" ""
+gdb_test "py search_buf = gdb.selected_frame ().read_var ('int64_search_buf')" "" ""
+gdb_test "py start_addr = search_buf.address" "" ""
+gdb_test "py length = search_buf.type.sizeof" "" ""
+gdb_test "py pattern = pack('Q', 0xfedcba9876543210)" "" ""
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, pattern)" \
+  "${one_pattern_found}" "find 64-bit pattern, with value 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 "py search_buf = gdb.selected_frame ().read_var ('search_buf')" "" ""
+gdb_test "py start_addr = search_buf\[0\].address" "" ""
+gdb_test "py pattern1 = pack('B', 0x62)" "" ""
+gdb_test "py pattern2 = pack('H', 0x6363)" "" ""
+gdb_test "py pattern3 = pack('I', 0x64646464)" "" ""
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, 100, pattern1)" \
+    "${one_pattern_found}" "find mixed-sized pattern"
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, 100, pattern2)" \
+    "${one_pattern_found}" "find mixed-sized pattern"
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, 100, pattern3)" \
+    "${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 ;
+
+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 "py start_addr = gdb.selected_frame ().read_var ('search_buf')" "" ""
+gdb_test "py length = gdb.selected_frame ().read_var ('search_buf_size')" "" ""
+gdb_test "py pattern = pack('I', 0x12345678)" "" ""
+gdb_test "py first = gdb.inferiors()\[0\].search_memory (start_addr,length, pattern)" "" ""
+gdb_test "py print first" "${one_pattern_found}" "search spanning large range 1st result"
+gdb_test "py start_addr = first + 1"
+gdb_test "py second = gdb.inferiors()\[0\].search_memory (start_addr, length, pattern)" "" ""
+gdb_test "py print second" "${one_pattern_found}" "search spanning large range 2nd result"
+gdb_test "py start_addr = second + 1"
+gdb_test "py third = gdb.inferiors()\[0\].search_memory (start_addr, length, pattern)" "" ""
+gdb_test "py print third" "${pattern_not_found}" "search spanning large range 3rd result"
+
+# 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 "py pattern = pack('I', 0xfdb97531)" "" ""
+    gdb_test "py start_addr = gdb.selected_frame ().read_var ('search_buf')" "" ""
+    gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, pattern)" \
+      "${one_pattern_found}" "find pattern straddling chunk boundary"
+}
diff --git a/gdb/testsuite/gdb.python/py-infthread.c b/gdb/testsuite/gdb.python/py-infthread.c
new file mode 100644
index 0000000..22eb9f2
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-infthread.c
@@ -0,0 +1,14 @@
+int f2 (int a)
+{
+  return ++a;
+}
+
+int f1 (int a, int b)
+{
+  return f2(a) + b;
+}
+
+int main (int argc, char *argv[])
+{
+  return f1 (1, 2);
+}
diff --git a/gdb/testsuite/gdb.python/py-infthread.exp b/gdb/testsuite/gdb.python/py-infthread.exp
new file mode 100644
index 0000000..a25d0b5
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-infthread.exp
@@ -0,0 +1,53 @@
+# Copyright (C) 2009, 2010 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 file is part of the GDB testsuite.  It tests the mechanism
+# exposing inferior threads to Python.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+set testfile "py-infthread"
+set srcfile ${testfile}.c
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
+    return -1
+}
+
+# Start with a fresh gdb.
+clean_restart ${testfile}
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+# The following tests require execution.
+
+if ![runto_main] then {
+    fail "Can't run to main"
+    return 0
+}
+
+runto [gdb_get_line_number "Break here."]
+
+# Test basic gdb.Inferior attributes and methods.
+
+gdb_py_test_silent_cmd "python t0 = gdb.selected_thread ()" "test gdb.selected_thread" 1
+gdb_test "python print t0" "\\<gdb.InferiorThread object at 0x\[\[:xdigit:\]\]+>" "verify InferiorThread object"
+gdb_test "python print 'result =', t0.num" " = \[0-9\]+" "test Inferior.num"
+gdb_test "python print 'result =', t0.ptid" " = \\(\[0-9\]+, \[0-9\]+, \[0-9\]+\\)" "test InferiorThread.ptid"
+
+gdb_test "python print 'result =', t0.is_stopped ()" " = True" "test InferiorThread.is_stopped"
+gdb_test "python print 'result =', t0.is_running ()" " = False" "test InferiorThread.is_running"
+gdb_test "python print 'result =', t0.is_exited ()" " = False" "test InferiorThread.is_exited"
diff --git a/gdb/value.c b/gdb/value.c
index 5e0e8d8..8812fe1 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -2244,6 +2244,43 @@ pack_long (gdb_byte *buf, struct type *type, LONGEST num)
 }
 
 
+/* Pack NUM into BUF using a target format of TYPE.  */
+
+void
+pack_unsigned_long (gdb_byte *buf, struct type *type, ULONGEST num)
+{
+  int len;
+  enum bfd_endian byte_order;
+
+  type = check_typedef (type);
+  len = TYPE_LENGTH (type);
+  byte_order = gdbarch_byte_order (get_type_arch (type));
+
+  switch (TYPE_CODE (type))
+    {
+    case TYPE_CODE_INT:
+    case TYPE_CODE_CHAR:
+    case TYPE_CODE_ENUM:
+    case TYPE_CODE_FLAGS:
+    case TYPE_CODE_BOOL:
+    case TYPE_CODE_RANGE:
+    case TYPE_CODE_MEMBERPTR:
+      store_unsigned_integer (buf, len, byte_order, num);
+      break;
+
+    case TYPE_CODE_REF:
+    case TYPE_CODE_PTR:
+      store_typed_address (buf, type, (CORE_ADDR) num);
+      break;
+
+    default:
+      error (_("\
+Unexpected type (%d) encountered for unsigned integer constant."),
+	     TYPE_CODE (type));
+    }
+}
+
+
 /* Convert C numbers into newly allocated values.  */
 
 struct value *
@@ -2256,6 +2293,19 @@ value_from_longest (struct type *type, LONGEST num)
 }
 
 
+/* Convert C unsigned numbers into newly allocated values.  */
+
+struct value *
+value_from_ulongest (struct type *type, ULONGEST num)
+{
+  struct value *val = allocate_value (type);
+
+  pack_unsigned_long (value_contents_raw (val), type, num);
+
+  return val;
+}
+
+
 /* Create a value representing a pointer of type TYPE to the address
    ADDR.  */
 struct value *
diff --git a/gdb/value.h b/gdb/value.h
index c3dfb17..474e661 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -366,6 +366,7 @@ extern LONGEST unpack_field_as_long (struct type *type,
 extern void pack_long (gdb_byte *buf, struct type *type, LONGEST num);
 
 extern struct value *value_from_longest (struct type *type, LONGEST num);
+extern struct value *value_from_ulongest (struct type *type, ULONGEST num);
 extern struct value *value_from_pointer (struct type *type, CORE_ADDR addr);
 extern struct value *value_from_double (struct type *type, DOUBLEST num);
 extern struct value *value_from_decfloat (struct type *type,

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [python][patch] Inferior and Thread information support.
  2010-06-22 10:32       ` Phil Muldoon
@ 2010-06-25 20:38         ` Tom Tromey
  2010-06-28  9:22           ` Phil Muldoon
  0 siblings, 1 reply; 21+ messages in thread
From: Tom Tromey @ 2010-06-25 20:38 UTC (permalink / raw)
  To: Phil Muldoon; +Cc: gdb-patches ml

>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:

Phil> I've done this.  Threads do not have the same registry mechanism yet, so
Phil> I've had to keep the old observer mechanism there.  But inferiors use
Phil> the new registry cleanup.

Phil> I've also simplified the search_memory api as requested.  We now take
Phil> an address, a length and a buffer only.  We now only return the first
Phil> match found in the buffer. (and let the user do the iteration in the
Phil> scripting side).

Phil> Latest patch attached. WDYT?

Phil> +PyObject *
Phil> +inferior_to_inferior_object (struct inferior *inferior)
Phil> +{
Phil> +  inferior_object *inf_obj;
Phil> +
Phil> +  inf_obj = inferior_data (inferior, infpy_inf_data_key);
Phil> +  if (!inf_obj)
Phil> +    {
Phil> +      struct cleanup *cleanup;
Phil> +      cleanup = ensure_python_env (python_gdbarch, python_language);
Phil> +
Phil> +      inf_obj = PyObject_New (inferior_object, &inferior_object_type);
[...]
Phil> +      do_cleanups (cleanup);
Phil> +    }
Phil> +  else
Phil> +    Py_INCREF (inf_obj);
Phil> +
Phil> +  return (PyObject *) inf_obj;

Right now the reference counting is inconsistent here.  Either a new
reference should be returned, and the Py_INCREF always done; or it
should return a borrowed reference, and this Py_INCREF removed.  I
prefer the borrowed reference approach.  I try to document this sort of
thing in the function's header comment.

Phil> +  cleanup = ensure_python_env (python_gdbarch, python_language);
Phil> +
Phil> +  thread_obj = create_thread_object (tp);
Phil> +  if (!thread_obj)
Phil> +    {
Phil> +      warning (_("Cannot create Python InferiorThread object."));
Phil> +      gdbpy_print_stack ();
Phil> +      do_cleanups (cleanup);

I don't think there is any need for a warning here.
The exception should suffice.

Phil> +static PyObject *
Phil> +infpy_get_was_attached (PyObject *self, void *closure)
Phil> +{
Phil> +  inferior_object *inf = (inferior_object *) self;
Phil> +  INFPY_REQUIRE_VALID (inf);

You need a blank line between these two lines now.

Phil> +static int
Phil> +build_inferior_list (struct inferior *inf, void *arg)
Phil> +{
Phil> +  PyObject *list = arg;
Phil> +  PyObject *inferior = inferior_to_inferior_object (inf);
Phil> +  PyList_Append (list, inferior);

Likewise.

Phil> +/* Implementation of gdb.read_memory (address, length).
Phil> +   Returns a Python buffer object with LENGTH bytes of the inferior's
Phil> +   memory at ADDRESS.  Both arguments are integers.  */
Phil> +static PyObject *
Phil> +infpy_read_memory (PyObject *self, PyObject *args)
Phil> +{
Phil> +  int error = 0;
Phil> +  CORE_ADDR addr, length;
Phil> +  void *buffer = NULL;
Phil> +  membuf_object *membuf_obj;
Phil> +  PyObject *addr_obj, *length_obj;
Phil> +  struct cleanup *cleanups;
Phil> +  volatile struct gdb_exception except;
Phil> +
Phil> +  if (! PyArg_ParseTuple (args, "OO", &addr_obj, &length_obj))
Phil> +    return NULL;

I hate to add on, but I think all functions taking 2 or more arguments
should accept keyword arguments.

Phil> +  cleanups = make_cleanup (null_cleanup, NULL);
Phil> +
Phil> +  TRY_CATCH (except, RETURN_MASK_ALL)
Phil> +    {
Phil> +      if (!get_addr_from_python (addr_obj, &addr)
Phil> +	  || !get_addr_from_python (length_obj, &length))
Phil> +	{
Phil> +	  error = 1;
Phil> +	  break;
Phil> +	}
Phil> +
Phil> +      buffer = xmalloc (length);
Phil> +      make_cleanup (xfree, buffer);
Phil> +
Phil> +      read_memory (addr, buffer, length);
Phil> +    }
Phil> +  GDB_PY_HANDLE_EXCEPTION (except);

GDB_PY_HANDLE_EXCEPTION returns from the function; this will leave the
cleanups dangling.  You have to run the cleanups before the return.

Phil> +static PyObject *
Phil> +infpy_write_memory (PyObject *self, PyObject *args)
Phil> +{
Phil> +  int buf_len, error = 0;
Phil> +  const char *buffer;
Phil> +  CORE_ADDR addr, length;
Phil> +  PyObject *addr_obj, *length_obj = NULL;
Phil> +  volatile struct gdb_exception except;
Phil> +
Phil> +  if (! PyArg_ParseTuple (args, "Os#|O", &addr_obj, &buffer, &buf_len,
Phil> +			  &length_obj))
Phil> +    return NULL;

Likewise about the keyword arguments.

Phil> +/* Implementation of InferiorThread.newest_frame () -> gdb.Frame.
Phil> +   Returns the newest frame object.  */
Phil> +PyObject *
Phil> +thpy_newest_frame (PyObject *self, PyObject *args)

This function should be static...

But, do we want it at all?  If the frame comes from some other thread,
there is almost no useful operation we can do with it.

That said I think the code for the function is fine.  If we have some
use case then I'm ok with it.

Tom

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [python][patch] Inferior and Thread information support.
  2010-06-18 20:10           ` Phil Muldoon
@ 2010-06-25 20:41             ` Tom Tromey
  0 siblings, 0 replies; 21+ messages in thread
From: Tom Tromey @ 2010-06-25 20:41 UTC (permalink / raw)
  To: Phil Muldoon; +Cc: Doug Evans, gdb-patches ml

>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:

>> Arguably, gdb.Value should support the buffer protocol.  I don't know if
>> that is directly possible, but if not we should supply a way to convert
>> a Value to a buffer -- to give the user a way to view the underlying
>> bits.

Phil> I don't disagree in principle.  I'm not sure this is within the
Phil> the scope of this patch though?
 
What you have is fine.

Phil> From a pragmatic point of view, between you and Doug I'm a little lost
Phil> about what we need to do to get this patch suitable for inclusion in
Phil> the FSF tree, with a possible view for inclusion to 7.2?  I'm not sure
Phil> what the removal of the max_count has to do with the other comments?
Phil> Making search_memory only support buffers is okay from the Python
Phil> side, that is easy.  What else needs to be done is my question ;)

For the record, I think you are very close; fix the nits and I am
satisfied.

Tom

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [python][patch] Inferior and Thread information support.
  2010-06-25 20:38         ` Tom Tromey
@ 2010-06-28  9:22           ` Phil Muldoon
  2010-06-28 19:51             ` Tom Tromey
  0 siblings, 1 reply; 21+ messages in thread
From: Phil Muldoon @ 2010-06-28  9:22 UTC (permalink / raw)
  To: tromey; +Cc: gdb-patches ml

On 06/25/2010 09:38 PM, Tom Tromey wrote:
>>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:

> Phil> Latest patch attached. WDYT?
> 
> Phil> +PyObject *
> Phil> +inferior_to_inferior_object (struct inferior *inferior)
> Phil> +{
> Phil> +  inferior_object *inf_obj;
> Phil> +
> Phil> +  inf_obj = inferior_data (inferior, infpy_inf_data_key);
> Phil> +  if (!inf_obj)
> Phil> +    {
> Phil> +      struct cleanup *cleanup;
> Phil> +      cleanup = ensure_python_env (python_gdbarch, python_language);
> Phil> +
> Phil> +      inf_obj = PyObject_New (inferior_object, &inferior_object_type);
> [...]
> Phil> +      do_cleanups (cleanup);
> Phil> +    }
> Phil> +  else
> Phil> +    Py_INCREF (inf_obj);
> Phil> +
> Phil> +  return (PyObject *) inf_obj;
> 
> Right now the reference counting is inconsistent here.


Ick, my apologies.  I inflicted this on the ported code recently for
testing and did not remove it.  Fixed.


 
> Phil> +  cleanup = ensure_python_env (python_gdbarch, python_language);
> Phil> +
> Phil> +  thread_obj = create_thread_object (tp);
> Phil> +  if (!thread_obj)
> Phil> +    {
> Phil> +      warning (_("Cannot create Python InferiorThread object."));
> Phil> +      gdbpy_print_stack ();
> Phil> +      do_cleanups (cleanup);
> 
> I don't think there is any need for a warning here.
> The exception should suffice.


OK
 
> Phil> +static PyObject *
> Phil> +infpy_get_was_attached (PyObject *self, void *closure)
> Phil> +{
> Phil> +  inferior_object *inf = (inferior_object *) self;
> Phil> +  INFPY_REQUIRE_VALID (inf);
> 
> You need a blank line between these two lines now.


OK, and for others.


> Phil> +/* Implementation of gdb.read_memory (address, length).
> Phil> +   Returns a Python buffer object with LENGTH bytes of the inferior's
> Phil> +   memory at ADDRESS.  Both arguments are integers.  */
> Phil> +static PyObject *
> Phil> +infpy_read_memory (PyObject *self, PyObject *args)
> Phil> +{
> 
> I hate to add on, but I think all functions taking 2 or more arguments
> should accept keyword arguments.


OK, and for others.


> Phil> +      buffer = xmalloc (length);
> Phil> +      make_cleanup (xfree, buffer);
> Phil> +
> Phil> +      read_memory (addr, buffer, length);
> Phil> +    }
> Phil> +  GDB_PY_HANDLE_EXCEPTION (except);
> 
> GDB_PY_HANDLE_EXCEPTION returns from the function; this will leave the
> cleanups dangling.  You have to run the cleanups before the return.


OK

  
> Phil> +/* Implementation of InferiorThread.newest_frame () -> gdb.Frame.
> Phil> +   Returns the newest frame object.  */
> Phil> +PyObject *
> Phil> +thpy_newest_frame (PyObject *self, PyObject *args)
> 
> This function should be static...
> 
> But, do we want it at all?  If the frame comes from some other thread,
> there is almost no useful operation we can do with it.
> 
> That said I think the code for the function is fine.  If we have some
> use case then I'm ok with it.


I just went ahead and deleted this.  Without the other frame related
inferior thread functions it does not make much sense to keep it
around.  So until we can figure out the thread -> frame -> switch
validity issue, I think the best thing is to rethink the API and
submit the frame functions in a future patch.


Updated patch attached.

Cheers,

Phil

--

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 5cb02d8..6dbb284 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -275,6 +275,8 @@ SUBDIR_PYTHON_OBS = \
 	py-cmd.o \
 	py-frame.o \
 	py-function.o \
+	py-inferior.o \
+	py-infthread.o \
 	py-lazy-string.o \
 	py-objfile.o \
 	py-param.o \
@@ -293,6 +295,8 @@ SUBDIR_PYTHON_SRCS = \
 	python/py-cmd.c \
 	python/py-frame.c \
 	python/py-function.c \
+	python/py-inferior.c \
+	python/py-infthread.c \
 	python/py-lazy-string.c \
 	python/py-objfile.c \
 	python/py-param.c \
@@ -2018,6 +2022,14 @@ py-function.o: $(srcdir)/python/py-function.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-function.c
 	$(POSTCOMPILE)
 
+py-inferior.o: $(srcdir)/python/py-inferior.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-inferior.c
+	$(POSTCOMPILE)
+
+py-infthread.o: $(srcdir)/python/py-infthread.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-infthread.c
+	$(POSTCOMPILE)
+
 py-lazy-string.o: $(srcdir)/python/py-lazy-string.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-lazy-string.c
 	$(POSTCOMPILE)
diff --git a/gdb/NEWS b/gdb/NEWS
index b286f1c..c42d1ab 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -133,9 +133,9 @@ is now deprecated.
 * Python scripting
 
 ** The GDB Python API now has access to breakpoints, symbols, symbol
-   tables, program spaces, and frame's code blocks.  Additionally, GDB
-   Parameters can now be created from the API, and manipulated via
-   set/show in the CLI.
+   tables, program spaces, inferiors, threads and frame's code blocks.
+   Additionally, GDB Parameters can now be created from the API, and
+   manipulated via set/show in the CLI.
 
 ** New functions gdb.target_charset, gdb.target_wide_charset,
    gdb.progspaces, gdb.current_progspace, and gdb.string_to_argv.
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 2ea1436..817e9b7 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -20175,6 +20175,8 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
 * Pretty Printing API::         Pretty-printing values.
 * Selecting Pretty-Printers::   How GDB chooses a pretty-printer.
 * Disabling Pretty-Printers::   Disabling broken printers.
+* Inferiors In Python::         Python representation of inferiors (processes)
+* Threads In Python::           Accessing inferior threads from Python.
 * Commands In Python::          Implementing new commands in Python.
 * Parameters In Python::        Adding new @value{GDBN} parameters.
 * Functions In Python::         Writing new convenience functions.
@@ -20996,6 +20998,132 @@ attribute to the registered function or callable object.  If this attribute
 is present and its value is @code{False}, the printer is disabled, otherwise
 the printer is enabled.
 
+@node Inferiors In Python
+@subsubsection Inferiors In Python
+@cindex inferiors in python
+
+@findex gdb.Inferior
+Programs which are being run under @value{GDBN} are called inferiors
+(@pxref{Inferiors and Programs}).  Python scripts can access
+information about and manipulate inferiors controlled by @value{GDBN}
+via objects of the @code{gdb.Inferior} class.
+
+The following inferior-related functions are available in the @code{gdb}
+module:
+
+@defun inferiors
+Return a tuple containing all inferior objects.
+@end defun
+
+A @code{gdb.Inferior} object has the following attributes:
+
+@table @code
+@defivar Inferior num
+ID of inferior, as assigned by GDB.
+@end defivar
+
+@defivar Inferior pid
+Process ID of the inferior, as assigned by the underlying operating
+system.
+@end defivar
+
+@defivar Inferior was_attached
+Boolean signaling whether the inferior was created using `attach', or
+started by @value{GDBN} itself.
+@end defivar
+@end table
+
+A @code{gdb.Inferior} object has the following methods:
+
+@table @code
+@defmethod Inferior threads
+This method returns a tuple holding all the threads which are valid
+when it is called.  If there are no valid threads, the method will
+return an empty tuple.
+@end defmethod
+
+@findex gdb.read_memory
+@defmethod Inferior read_memory address length
+Read @var{length} bytes of memory from the inferior, starting at
+@var{address}.  Returns a buffer object, which behaves much like an array
+or a string.  It can be modified and given to the @code{gdb.write_memory}
+function.
+@end defmethod
+
+@findex gdb.write_memory
+@defmethod Inferior write_memory address buffer @r{[}length@r{]}
+Write the contents of @var{buffer} to the inferior, starting at
+@var{address}.  The @var{buffer} parameter must be a Python object
+which supports the buffer protocol, i.e., a string, an array or the
+object returned from @code{gdb.read_memory}.  If given, @var{length}
+determines the number of bytes from @var{buffer} to be written.
+@end defmethod
+
+@findex gdb.search_memory
+@defmethod Inferior search_memory address length pattern
+Search a region of the inferior memory starting at @var{address} with
+the given @var{length} using the search pattern supplied in
+@var{pattern}.  The @var{pattern} parameter must be a Python object
+which supports the buffer protocol, i.e., a string, an array or the
+object returned from @code{gdb.read_memory}.  Returns a Python @code{Long}
+containing the address where the pattern was found, or @code{None} if
+the pattern could not be found.
+@end defmethod
+@end table
+
+@node Threads In Python
+@subsubsection Threads In Python
+@cindex threads in python
+
+@findex gdb.InferiorThread
+Python scripts can access information about, and manipulate inferior threads
+controlled by @value{GDBN}, via objects of the @code{gdb.InferiorThread} class.
+
+The following thread-related functions are available in the @code{gdb}
+module:
+
+@findex gdb.selected_thread
+@defun selected_thread
+This function returns the thread object for the selected thread.  If there
+is no selected thread, this will return @code{None}.
+@end defun
+
+A @code{gdb.InferiorThread} object has the following attributes:
+
+@table @code
+@defivar InferiorThread num
+ID of the thread, as assigned by GDB.
+@end defivar
+
+@defivar InferiorThread ptid
+ID of the thread, as assigned by the operating system.  This attribute is a
+tuple containing three integers.  The first is the Process ID (PID); the second
+is the Lightweight Process ID (LWPID), and the third is the Thread ID (TID).
+Either the LWPID or TID may be 0, which indicates that the operating system
+does not  use that identifier.
+@end defivar
+@end table
+
+A @code{gdb.InferiorThread} object has the following methods:
+
+@defmethod InferiorThread switch
+This changes @value{GDBN}'s currently selected thread to the one represented
+by this object.
+@end defmethod
+
+@defmethod InferiorThread is_stopped
+Return a Boolean indicating whether the thread is stopped.
+@end defmethod
+
+@defmethod InferiorThread is_running
+Return a Boolean indicating whether the thread is running.
+@end defmethod
+
+@defmethod InferiorThread is_exited
+Return a Boolean indicating whether the thread is exited.
+@end defmethod
+@end table
+
 @node Commands In Python
 @subsubsection Commands In Python
 
diff --git a/gdb/python/py-frame.c b/gdb/python/py-frame.c
index 6e9e5d3..a7df810 100644
--- a/gdb/python/py-frame.c
+++ b/gdb/python/py-frame.c
@@ -265,7 +265,7 @@ frapy_function (PyObject *self, PyObject *args)
 /* Convert a frame_info struct to a Python Frame object.
    Sets a Python exception and returns NULL on error.  */
 
-static frame_object *
+PyObject *
 frame_info_to_frame_object (struct frame_info *frame)
 {
   frame_object *frame_obj;
@@ -296,7 +296,7 @@ frame_info_to_frame_object (struct frame_info *frame)
 
   frame_obj->gdbarch = get_frame_arch (frame);
 
-  return frame_obj;
+  return (PyObject *) frame_obj;
 }
 
 /* Implementation of gdb.Frame.older (self) -> gdb.Frame.
@@ -497,7 +497,7 @@ PyObject *
 gdbpy_selected_frame (PyObject *self, PyObject *args)
 {
   struct frame_info *frame;
-  frame_object *frame_obj = NULL;   /* Initialize to appease gcc warning.  */
+  PyObject *frame_obj = NULL;   /* Initialize to appease gcc warning.  */
   volatile struct gdb_exception except;
 
   TRY_CATCH (except, RETURN_MASK_ALL)
@@ -507,7 +507,7 @@ gdbpy_selected_frame (PyObject *self, PyObject *args)
     }
   GDB_PY_HANDLE_EXCEPTION (except);
 
-  return (PyObject *) frame_obj;
+  return frame_obj;
 }
 
 /* Implementation of gdb.stop_reason_string (Integer) -> String.
diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c
new file mode 100644
index 0000000..3257eb9
--- /dev/null
+++ b/gdb/python/py-inferior.c
@@ -0,0 +1,727 @@
+/* Python interface to inferiors.
+
+   Copyright (C) 2009, 2010 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 "exceptions.h"
+#include "gdbcore.h"
+#include "gdbthread.h"
+#include "inferior.h"
+#include "observer.h"
+#include "python-internal.h"
+#include "arch-utils.h"
+#include "language.h"
+
+struct threadlist_entry {
+  thread_object *thread_obj;
+  struct threadlist_entry *next;
+};
+
+typedef struct
+{
+  PyObject_HEAD
+
+  /* The inferior we represent.  */
+  struct inferior *inferior;
+
+  /* thread_object instances under this inferior.  This list owns a
+     reference to each object it contains.  */
+  struct threadlist_entry *threads;
+
+  /* Number of threads in the list.  */
+  int nthreads;
+} inferior_object;
+
+static PyTypeObject inferior_object_type;
+
+static const struct inferior_data *infpy_inf_data_key;
+
+typedef struct {
+  PyObject_HEAD
+  void *buffer;
+
+  /* These are kept just for mbpy_str.  */
+  CORE_ADDR addr;
+  CORE_ADDR length;
+} membuf_object;
+
+static PyTypeObject membuf_object_type;
+
+/* Require that INFERIOR be a valid inferior ID.  */
+#define INFPY_REQUIRE_VALID(Inferior)				\
+  do {								\
+    if (!Inferior->inferior)					\
+      {								\
+	PyErr_SetString (PyExc_RuntimeError,			\
+			 _("Inferior no longer exists."));	\
+	return NULL;						\
+      }								\
+  } while (0)
+
+/* Return a borrowed reference to the Python object of type Inferior
+   representing INFERIOR.  If the object has already been created,
+   return it,  otherwise, create it.  Return NULL on failure.  */
+PyObject *
+inferior_to_inferior_object (struct inferior *inferior)
+{
+  inferior_object *inf_obj;
+
+  inf_obj = inferior_data (inferior, infpy_inf_data_key);
+  if (!inf_obj)
+    {
+      struct cleanup *cleanup;
+      cleanup = ensure_python_env (python_gdbarch, python_language);
+
+      inf_obj = PyObject_New (inferior_object, &inferior_object_type);
+      if (!inf_obj)
+	{
+	  do_cleanups (cleanup);
+	  return NULL;
+	}
+
+      inf_obj->inferior = inferior;
+      inf_obj->threads = NULL;
+      inf_obj->nthreads = 0;
+
+      set_inferior_data (inferior, infpy_inf_data_key, inf_obj);
+
+      do_cleanups (cleanup);
+    }
+
+  return (PyObject *) inf_obj;
+}
+
+/* Finds the Python Inferior object for the given PID.  Returns a
+   borrowed reference, or NULL if PID does not match any inferior
+   obect.
+  */
+PyObject *
+find_inferior_object (int pid)
+{
+  struct inflist_entry *p;
+  struct inferior *inf = find_inferior_pid (pid);
+
+  if (inf)
+    return inferior_to_inferior_object (inf);
+
+  return NULL;
+}
+
+thread_object *
+find_thread_object (ptid_t ptid)
+{
+  int pid;
+  struct threadlist_entry *thread;
+  PyObject *inf_obj;
+
+  pid = PIDGET (ptid);
+  inf_obj = find_inferior_object (pid);
+
+  if (inf_obj)
+    for (thread = ((inferior_object *)inf_obj)->threads; thread;
+	 thread = thread->next)
+      if (ptid_equal (thread->thread_obj->thread->ptid, ptid))
+	return thread->thread_obj;
+
+  return NULL;
+}
+
+static void
+add_thread_object (struct thread_info *tp)
+{
+  struct cleanup *cleanup;
+  thread_object *thread_obj;
+  inferior_object *inf_obj;
+  struct threadlist_entry *entry;
+
+  cleanup = ensure_python_env (python_gdbarch, python_language);
+
+  thread_obj = create_thread_object (tp);
+  if (!thread_obj)
+    {
+      do_cleanups (cleanup);
+      return;
+    }
+
+  inf_obj = (inferior_object *) thread_obj->inf_obj;
+
+  entry = xmalloc (sizeof (struct threadlist_entry));
+  entry->thread_obj = thread_obj;
+  entry->next = inf_obj->threads;
+
+  inf_obj->threads = entry;
+  inf_obj->nthreads++;
+
+  do_cleanups (cleanup);
+}
+
+static void
+delete_thread_object (struct thread_info *tp, int ignore)
+{
+  struct cleanup *cleanup;
+  inferior_object *inf_obj;
+  thread_object *thread_obj;
+  struct threadlist_entry **entry, *tmp;
+
+  inf_obj = (inferior_object *) find_inferior_object (PIDGET(tp->ptid));
+  if (!inf_obj)
+    return;
+
+  /* Find thread entry in its inferior's thread_list.  */
+  for (entry = &inf_obj->threads; *entry != NULL; entry =
+	 &(*entry)->next)
+    if ((*entry)->thread_obj->thread == tp)
+      break;
+
+  if (!*entry)
+    return;
+
+  cleanup = ensure_python_env (python_gdbarch, python_language);
+
+  tmp = *entry;
+  tmp->thread_obj->thread = NULL;
+
+  *entry = (*entry)->next;
+  inf_obj->nthreads--;
+
+  Py_DECREF (tmp->thread_obj);
+  xfree (tmp);
+
+  do_cleanups (cleanup);
+}
+
+static PyObject *
+infpy_threads (PyObject *self, PyObject *args)
+{
+  int i;
+  struct threadlist_entry *entry;
+  inferior_object *inf_obj = (inferior_object *) self;
+  PyObject *tuple;
+
+  INFPY_REQUIRE_VALID (inf_obj);
+
+  tuple = PyTuple_New (inf_obj->nthreads);
+  if (!tuple)
+    return NULL;
+
+  for (i = 0, entry = inf_obj->threads; i < inf_obj->nthreads;
+       i++, entry = entry->next)
+    {
+      Py_INCREF (entry->thread_obj);
+      PyTuple_SET_ITEM (tuple, i, (PyObject *) entry->thread_obj);
+    }
+
+  return tuple;
+}
+
+static PyObject *
+infpy_get_num (PyObject *self, void *closure)
+{
+  inferior_object *inf = (inferior_object *) self;
+
+  INFPY_REQUIRE_VALID (inf);
+
+  return PyLong_FromLong (inf->inferior->num);
+}
+
+static PyObject *
+infpy_get_pid (PyObject *self, void *closure)
+{
+  inferior_object *inf = (inferior_object *) self;
+
+  INFPY_REQUIRE_VALID (inf);
+
+  return PyLong_FromLong (inf->inferior->pid);
+}
+
+static PyObject *
+infpy_get_was_attached (PyObject *self, void *closure)
+{
+  inferior_object *inf = (inferior_object *) self;
+
+  INFPY_REQUIRE_VALID (inf);
+  if (inf->inferior->attach_flag)
+    Py_RETURN_TRUE;
+  Py_RETURN_FALSE;
+}
+
+static int
+build_inferior_list (struct inferior *inf, void *arg)
+{
+  PyObject *list = arg;
+  PyObject *inferior = inferior_to_inferior_object (inf);
+
+  PyList_Append (list, inferior);
+  return 0;
+}
+
+/* Implementation of gdb.inferiors () -> (gdb.Inferior, ...).
+   Returns a tuple of all inferiors.  */
+PyObject *
+gdbpy_inferiors (PyObject *unused, PyObject *unused2)
+{
+  int i = 0;
+  PyObject *list, *inferior;
+  struct inferior *inf;
+
+  list = PyList_New (0);
+  if (!list)
+    return NULL;
+
+  iterate_over_inferiors (build_inferior_list, list);
+
+  return PyList_AsTuple (list);
+}
+
+/* Membuf and memory manipulation.  */
+
+/* Implementation of gdb.read_memory (address, length).
+   Returns a Python buffer object with LENGTH bytes of the inferior's
+   memory at ADDRESS.  Both arguments are integers.  */
+static PyObject *
+infpy_read_memory (PyObject *self, PyObject *args, PyObject *kw)
+{
+  int error = 0;
+  CORE_ADDR addr, length;
+  void *buffer = NULL;
+  membuf_object *membuf_obj;
+  PyObject *addr_obj, *length_obj;
+  struct cleanup *cleanups;
+  volatile struct gdb_exception except;
+  static char *keywords[] = { "address", "length", NULL };
+
+  if (! PyArg_ParseTupleAndKeywords (args, kw, "OO", keywords,
+				     &addr_obj, &length_obj))
+    return NULL;
+
+  cleanups = make_cleanup (null_cleanup, NULL);
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      if (!get_addr_from_python (addr_obj, &addr)
+	  || !get_addr_from_python (length_obj, &length))
+	{
+	  error = 1;
+	  break;
+	}
+
+      buffer = xmalloc (length);
+      make_cleanup (xfree, buffer);
+
+      read_memory (addr, buffer, length);
+    }
+  if (except.reason < 0)
+    {
+      do_cleanups (cleanups);
+      return PyErr_Format (PyExc_RuntimeError, "%s", except.message);
+    }
+
+  if (error)
+    {
+      do_cleanups (cleanups);
+      return NULL;
+    }
+
+  membuf_obj = PyObject_New (membuf_object, &membuf_object_type);
+  if (membuf_obj == NULL)
+    {
+      PyErr_SetString (PyExc_MemoryError,
+		       _("Could not allocate memory buffer object."));
+      do_cleanups (cleanups);
+      return NULL;
+    }
+
+  discard_cleanups (cleanups);
+
+  membuf_obj->buffer = buffer;
+  membuf_obj->addr = addr;
+  membuf_obj->length = length;
+
+  return PyBuffer_FromReadWriteObject ((PyObject *) membuf_obj, 0,
+				       Py_END_OF_BUFFER);
+}
+
+/* Implementation of gdb.write_memory (address, buffer [, length]).
+   Writes the contents of BUFFER (a Python object supporting the read
+   buffer protocol) at ADDRESS in the inferior's memory.  Write LENGTH
+   bytes from BUFFER, or its entire contents if the argument is not
+   provided.  The function returns nothing.  */
+static PyObject *
+infpy_write_memory (PyObject *self, PyObject *args, PyObject *kw)
+{
+  int buf_len, error = 0;
+  const char *buffer;
+  CORE_ADDR addr, length;
+  PyObject *addr_obj, *length_obj = NULL;
+  volatile struct gdb_exception except;
+  static char *keywords[] = { "address", "buffer", "length", NULL };
+
+
+  if (! PyArg_ParseTupleAndKeywords (args, kw, "Os#|O", keywords,
+				     &addr_obj, &buffer, &buf_len,
+				     &length_obj))
+    return NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      if (!get_addr_from_python (addr_obj, &addr))
+	{
+	  error = 1;
+	  break;
+	}
+
+      if (!length_obj)
+	length = buf_len;
+      else if (!get_addr_from_python (length_obj, &length))
+	{
+	  error = 1;
+	  break;
+	}
+      write_memory (addr, buffer, length);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  if (error)
+    return NULL;
+
+  Py_RETURN_NONE;
+}
+
+/* Destructor of Membuf objects.  */
+static void
+mbpy_dealloc (PyObject *self)
+{
+  xfree (((membuf_object *) self)->buffer);
+  self->ob_type->tp_free (self);
+}
+
+/* Return a description of the Membuf object.  */
+static PyObject *
+mbpy_str (PyObject *self)
+{
+  membuf_object *membuf_obj = (membuf_object *) self;
+
+  return PyString_FromFormat (_("Memory buffer for address %s, \
+which is %s bytes long."),
+			      paddress (python_gdbarch, membuf_obj->addr),
+			      pulongest (membuf_obj->length));
+}
+
+static Py_ssize_t
+get_read_buffer (PyObject *self, Py_ssize_t segment, void **ptrptr)
+{
+  membuf_object *membuf_obj = (membuf_object *) self;
+
+  if (segment)
+    {
+      PyErr_SetString (PyExc_SystemError,
+		       _("The memory buffer supports only one segment."));
+      return -1;
+    }
+
+  *ptrptr = membuf_obj->buffer;
+
+  return membuf_obj->length;
+}
+
+static Py_ssize_t
+get_write_buffer (PyObject *self, Py_ssize_t segment, void **ptrptr)
+{
+  return get_read_buffer (self, segment, ptrptr);
+}
+
+static Py_ssize_t
+get_seg_count (PyObject *self, Py_ssize_t *lenp)
+{
+  if (lenp)
+    *lenp = ((membuf_object *) self)->length;
+
+  return 1;
+}
+
+static Py_ssize_t
+get_char_buffer (PyObject *self, Py_ssize_t segment, char **ptrptr)
+{
+  void *ptr = NULL;
+  Py_ssize_t ret;
+
+  ret = get_read_buffer (self, segment, &ptr);
+  *ptrptr = (char *) ptr;
+
+  return ret;
+}
+
+/* Implementation of
+   gdb.search_memory (address, length, pattern).  ADDRESS is the
+   address to start the search.  LENGTH specifies the scope of the
+   search from ADDRESS.  PATTERN is the pattern to search for (and
+   must be a Python object supporting the buffer protocol).
+   Returns a Python Long object holding the address where the pattern
+   was located, or if the pattern was not found, returns None.  */
+static PyObject *
+infpy_search_memory (PyObject *self, PyObject *args, PyObject *kw)
+{
+  CORE_ADDR start_addr, length;
+  static char *keywords[] = { "address", "length", "pattern", NULL };
+  PyObject *pattern, *start_addr_obj, *length_obj;
+  volatile struct gdb_exception except;
+  Py_ssize_t pattern_size;
+  const void *buffer;
+  CORE_ADDR found_addr;
+  int found = 0;
+
+  if (! PyArg_ParseTupleAndKeywords (args, kw, "OOO", keywords,
+				     &start_addr_obj, &length_obj,
+				     &pattern))
+    return NULL;
+
+  if (get_addr_from_python (start_addr_obj, &start_addr)
+      && get_addr_from_python (length_obj, &length))
+    {
+      if (!length)
+	{
+	  PyErr_SetString (PyExc_ValueError,
+			   _("Search range is empty."));
+	  return NULL;
+	}
+      /* Watch for overflows.  */
+      else if (length > CORE_ADDR_MAX
+	       || (start_addr + length - 1) < start_addr)
+	{
+	  PyErr_SetString (PyExc_ValueError,
+			   _("The search range is too large."));
+
+	  return NULL;
+	}
+    }
+  else
+    {
+      PyErr_SetString (PyExc_RuntimeError,
+		       _("Cannot get search address/range from Python."));
+
+      return NULL;
+    }
+
+  if (!PyObject_CheckReadBuffer (pattern))
+    {
+      PyErr_SetString (PyExc_RuntimeError,
+		       _("The pattern is not a Python buffer."));
+
+      return NULL;
+    }
+
+  if (PyObject_AsReadBuffer (pattern, &buffer, &pattern_size) == -1)
+    return NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      found = target_search_memory (start_addr, length,
+				    buffer, pattern_size,
+				    &found_addr);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  if (found)
+    return PyLong_FromLong (found_addr);
+  else
+    Py_RETURN_NONE;
+}
+
+
+/* Clear the INFERIOR pointer in an Inferior object and clear the
+   thread list.  */
+static void
+py_free_inferior (struct inferior *inf, void *datum)
+{
+
+  struct cleanup *cleanup;
+  inferior_object *inf_obj = datum;
+  struct threadlist_entry *th_entry, *th_tmp;
+
+  cleanup = ensure_python_env (python_gdbarch, python_language);
+
+  inf_obj->inferior = NULL;
+
+  /* Deallocate threads list.  */
+  for (th_entry = inf_obj->threads; th_entry != NULL;)
+    {
+      Py_DECREF (th_entry->thread_obj);
+
+      th_tmp = th_entry;
+      th_entry = th_entry->next;
+      xfree (th_tmp);
+    }
+
+  inf_obj->nthreads = 0;
+
+  Py_DECREF ((PyObject *) inf_obj);
+  do_cleanups (cleanup);
+}
+
+void
+gdbpy_initialize_inferior (void)
+{
+  if (PyType_Ready (&inferior_object_type) < 0)
+    return;
+
+  Py_INCREF (&inferior_object_type);
+  PyModule_AddObject (gdb_module, "Inferior",
+		      (PyObject *) &inferior_object_type);
+
+  infpy_inf_data_key =
+    register_inferior_data_with_cleanup (py_free_inferior);
+
+  observer_attach_new_thread (add_thread_object);
+  observer_attach_thread_exit (delete_thread_object);
+
+  if (PyType_Ready (&membuf_object_type) < 0)
+    return;
+
+  Py_INCREF (&membuf_object_type);
+  PyModule_AddObject (gdb_module, "Membuf", (PyObject *)
+		      &membuf_object_type);
+}
+
+static PyGetSetDef inferior_object_getset[] =
+{
+  { "num", infpy_get_num, NULL, "ID of inferior, as assigned by GDB.", NULL },
+  { "pid", infpy_get_pid, NULL, "PID of inferior, as assigned by the OS.",
+    NULL },
+  { "was_attached", infpy_get_was_attached, NULL,
+    "True if the inferior was created using 'attach'.", NULL },
+  { NULL }
+};
+
+static PyMethodDef inferior_object_methods[] =
+{
+  { "threads", infpy_threads, METH_NOARGS,
+    "Return all the threads of this inferior." },
+  { "read_memory", (PyCFunction) infpy_read_memory,
+    METH_VARARGS | METH_KEYWORDS,
+    "read_memory (address, length) -> buffer\n\
+Return a buffer object for reading from the inferior's memory." },
+  { "write_memory", (PyCFunction) infpy_write_memory,
+    METH_VARARGS | METH_KEYWORDS,
+    "write_memory (address, buffer [, length])\n\
+Write the given buffer object to the inferior's memory." },
+  { "search_memory", (PyCFunction) infpy_search_memory,
+    METH_VARARGS | METH_KEYWORDS,
+    "search_memory (address, length, pattern) -> long\n\
+Return a long with the address of a match, or None." },
+  { NULL }
+};
+
+static PyTypeObject inferior_object_type =
+{
+  PyObject_HEAD_INIT (NULL)
+  0,				  /* ob_size */
+  "gdb.Inferior",		  /* tp_name */
+  sizeof (inferior_object),	  /* tp_basicsize */
+  0,				  /* tp_itemsize */
+  0,				  /* tp_dealloc */
+  0,				  /* tp_print */
+  0,				  /* tp_getattr */
+  0,				  /* tp_setattr */
+  0,				  /* tp_compare */
+  0,				  /* tp_repr */
+  0,				  /* tp_as_number */
+  0,				  /* tp_as_sequence */
+  0,				  /* tp_as_mapping */
+  0,				  /* tp_hash  */
+  0,				  /* tp_call */
+  0,				  /* tp_str */
+  0,				  /* tp_getattro */
+  0,				  /* tp_setattro */
+  0,				  /* tp_as_buffer */
+  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER,  /* tp_flags */
+  "GDB inferior object",	  /* tp_doc */
+  0,				  /* tp_traverse */
+  0,				  /* tp_clear */
+  0,				  /* tp_richcompare */
+  0,				  /* tp_weaklistoffset */
+  0,				  /* tp_iter */
+  0,				  /* tp_iternext */
+  inferior_object_methods,	  /* tp_methods */
+  0,				  /* tp_members */
+  inferior_object_getset,	  /* tp_getset */
+  0,				  /* tp_base */
+  0,				  /* tp_dict */
+  0,				  /* tp_descr_get */
+  0,				  /* tp_descr_set */
+  0,				  /* tp_dictoffset */
+  0,				  /* tp_init */
+  0				  /* tp_alloc */
+};
+
+/* Python doesn't provide a decent way to get compatibility here.  */
+#if HAVE_LIBPYTHON2_4
+#define CHARBUFFERPROC_NAME getcharbufferproc
+#else
+#define CHARBUFFERPROC_NAME charbufferproc
+#endif
+
+static PyBufferProcs buffer_procs = {
+  get_read_buffer,
+  get_write_buffer,
+  get_seg_count,
+  /* The cast here works around a difference between Python 2.4 and
+     Python 2.5.  */
+  (CHARBUFFERPROC_NAME) get_char_buffer
+};
+
+static PyTypeObject membuf_object_type = {
+  PyObject_HEAD_INIT (NULL)
+  0,				  /*ob_size*/
+  "gdb.Membuf",			  /*tp_name*/
+  sizeof (membuf_object),	  /*tp_basicsize*/
+  0,				  /*tp_itemsize*/
+  mbpy_dealloc,			  /*tp_dealloc*/
+  0,				  /*tp_print*/
+  0,				  /*tp_getattr*/
+  0,				  /*tp_setattr*/
+  0,				  /*tp_compare*/
+  0,				  /*tp_repr*/
+  0,				  /*tp_as_number*/
+  0,				  /*tp_as_sequence*/
+  0,				  /*tp_as_mapping*/
+  0,				  /*tp_hash */
+  0,				  /*tp_call*/
+  mbpy_str,			  /*tp_str*/
+  0,				  /*tp_getattro*/
+  0,				  /*tp_setattro*/
+  &buffer_procs,		  /*tp_as_buffer*/
+  Py_TPFLAGS_DEFAULT,		  /*tp_flags*/
+  "GDB memory buffer object", 	  /*tp_doc*/
+  0,				  /* tp_traverse */
+  0,				  /* tp_clear */
+  0,				  /* tp_richcompare */
+  0,				  /* tp_weaklistoffset */
+  0,				  /* tp_iter */
+  0,				  /* tp_iternext */
+  0,				  /* tp_methods */
+  0,				  /* tp_members */
+  0,				  /* tp_getset */
+  0,				  /* tp_base */
+  0,				  /* tp_dict */
+  0,				  /* tp_descr_get */
+  0,				  /* tp_descr_set */
+  0,				  /* tp_dictoffset */
+  0,				  /* tp_init */
+  0,				  /* tp_alloc */
+  PyType_GenericNew		  /* tp_new */
+};
diff --git a/gdb/python/py-infthread.c b/gdb/python/py-infthread.c
new file mode 100644
index 0000000..86aba50
--- /dev/null
+++ b/gdb/python/py-infthread.c
@@ -0,0 +1,269 @@
+/* Python interface to inferior threads.
+
+   Copyright (C) 2009, 2010 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 "exceptions.h"
+#include "gdbthread.h"
+#include "inferior.h"
+#include "python-internal.h"
+
+static PyTypeObject thread_object_type;
+
+/* Require that INFERIOR be a valid inferior ID.  */
+#define THPY_REQUIRE_VALID(Thread)				\
+  do {								\
+    if (!Thread->thread)					\
+      {								\
+	PyErr_SetString (PyExc_RuntimeError,			\
+			 _("Thread no longer exists."));	\
+	return NULL;						\
+      }								\
+  } while (0)
+
+
+
+thread_object *
+create_thread_object (struct thread_info *tp)
+{
+  thread_object *thread_obj;
+
+  thread_obj = PyObject_New (thread_object, &thread_object_type);
+  if (!thread_obj)
+    return NULL;
+
+  thread_obj->thread = tp;
+  thread_obj->inf_obj = find_inferior_object (PIDGET (tp->ptid));
+  Py_INCREF (thread_obj->inf_obj);
+
+  return thread_obj;
+}
+
+
+
+static void
+thpy_dealloc (PyObject *self)
+{
+  Py_DECREF (((thread_object *) self)->inf_obj);
+  self->ob_type->tp_free (self);
+}
+
+static PyObject *
+thpy_get_num (PyObject *self, void *closure)
+{
+  thread_object *thread_obj = (thread_object *) self;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  return PyLong_FromLong (thread_obj->thread->num);
+}
+
+/* Getter for InferiorThread.ptid  -> (pid, lwp, tid).
+   Returns a tuple with the thread's ptid components.  */
+static PyObject *
+thpy_get_ptid (PyObject *self, void *closure)
+{
+  int pid;
+  long tid, lwp;
+  thread_object *thread_obj = (thread_object *) self;
+  PyObject *ret;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  ret = PyTuple_New (3);
+  if (!ret)
+    return NULL;
+
+  pid = ptid_get_pid (thread_obj->thread->ptid);
+  lwp = ptid_get_lwp (thread_obj->thread->ptid);
+  tid = ptid_get_tid (thread_obj->thread->ptid);
+
+  PyTuple_SET_ITEM (ret, 0, PyInt_FromLong (pid));
+  PyTuple_SET_ITEM (ret, 1, PyInt_FromLong (lwp));
+  PyTuple_SET_ITEM (ret, 2, PyInt_FromLong (tid));
+
+  return ret;
+}
+
+/* Implementation of InferiorThread.switch ().
+   Makes this the GDB selected thread.  */
+static PyObject *
+thpy_switch (PyObject *self, PyObject *args)
+{
+  thread_object *thread_obj = (thread_object *) self;
+  struct cleanup *cleanup;
+  volatile struct gdb_exception except;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      switch_to_thread (thread_obj->thread->ptid);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  Py_RETURN_NONE;
+}
+
+/* Implementation of InferiorThread.is_stopped () -> Boolean.
+   Return whether the thread is stopped.  */
+static PyObject *
+thpy_is_stopped (PyObject *self, PyObject *args)
+{
+  thread_object *thread_obj = (thread_object *) self;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  if (is_stopped (thread_obj->thread->ptid))
+    Py_RETURN_TRUE;
+
+  Py_RETURN_FALSE;
+}
+
+/* Implementation of InferiorThread.is_running () -> Boolean.
+   Return whether the thread is running.  */
+static PyObject *
+thpy_is_running (PyObject *self, PyObject *args)
+{
+  thread_object *thread_obj = (thread_object *) self;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  if (is_running (thread_obj->thread->ptid))
+    Py_RETURN_TRUE;
+
+  Py_RETURN_FALSE;
+}
+
+/* Implementation of InferiorThread.is_exited () -> Boolean.
+   Return whether the thread is exited.  */
+static PyObject *
+thpy_is_exited (PyObject *self, PyObject *args)
+{
+  thread_object *thread_obj = (thread_object *) self;
+
+  THPY_REQUIRE_VALID (thread_obj);
+
+  if (is_exited (thread_obj->thread->ptid))
+    Py_RETURN_TRUE;
+
+  Py_RETURN_FALSE;
+}
+
+
+
+/* Implementation of gdb.selected_thread () -> gdb.InferiorThread.
+   Returns the selected thread object.  */
+PyObject *
+gdbpy_selected_thread (PyObject *self, PyObject *args)
+{
+  PyObject *thread_obj;
+
+  thread_obj = (PyObject *) find_thread_object (inferior_ptid);
+  if (thread_obj)
+    {
+      Py_INCREF (thread_obj);
+      return thread_obj;
+    }
+
+  Py_RETURN_NONE;
+}
+
+
+
+void
+gdbpy_initialize_thread (void)
+{
+  if (PyType_Ready (&thread_object_type) < 0)
+    return;
+
+  Py_INCREF (&thread_object_type);
+  PyModule_AddObject (gdb_module, "InferiorThread",
+		      (PyObject *) &thread_object_type);
+}
+
+
+
+static PyGetSetDef thread_object_getset[] =
+{
+  { "num", thpy_get_num, NULL, "ID of the thread, as assigned by GDB.", NULL },
+  { "ptid", thpy_get_ptid, NULL, "ID of the thread, as assigned by the OS.",
+    NULL },
+
+  { NULL }
+};
+
+static PyMethodDef thread_object_methods[] =
+{
+  { "switch", thpy_switch, METH_NOARGS,
+    "switch ()\n\
+Makes this the GDB selected thread." },
+  { "is_stopped", thpy_is_stopped, METH_NOARGS,
+    "is_stopped () -> Boolean\n\
+Return whether the thread is stopped." },
+  { "is_running", thpy_is_running, METH_NOARGS,
+    "is_running () -> Boolean\n\
+Return whether the thread is running." },
+  { "is_exited", thpy_is_exited, METH_NOARGS,
+    "is_exited () -> Boolean\n\
+Return whether the thread is exited." },
+
+  { NULL }
+};
+
+static PyTypeObject thread_object_type =
+{
+  PyObject_HEAD_INIT (NULL)
+  0,				  /*ob_size*/
+  "gdb.InferiorThread",		  /*tp_name*/
+  sizeof (thread_object),	  /*tp_basicsize*/
+  0,				  /*tp_itemsize*/
+  thpy_dealloc,			  /*tp_dealloc*/
+  0,				  /*tp_print*/
+  0,				  /*tp_getattr*/
+  0,				  /*tp_setattr*/
+  0,				  /*tp_compare*/
+  0,				  /*tp_repr*/
+  0,				  /*tp_as_number*/
+  0,				  /*tp_as_sequence*/
+  0,				  /*tp_as_mapping*/
+  0,				  /*tp_hash */
+  0,				  /*tp_call*/
+  0,				  /*tp_str*/
+  0,				  /*tp_getattro*/
+  0,				  /*tp_setattro*/
+  0,				  /*tp_as_buffer*/
+  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER,  /*tp_flags*/
+  "GDB thread object",		  /* tp_doc */
+  0,				  /* tp_traverse */
+  0,				  /* tp_clear */
+  0,				  /* tp_richcompare */
+  0,				  /* tp_weaklistoffset */
+  0,				  /* tp_iter */
+  0,				  /* tp_iternext */
+  thread_object_methods,	  /* tp_methods */
+  0,				  /* tp_members */
+  thread_object_getset,		  /* tp_getset */
+  0,				  /* tp_base */
+  0,				  /* tp_dict */
+  0,				  /* tp_descr_get */
+  0,				  /* tp_descr_set */
+  0,				  /* tp_dictoffset */
+  0,				  /* tp_init */
+  0				  /* tp_alloc */
+};
diff --git a/gdb/python/py-utils.c b/gdb/python/py-utils.c
index 944e6b4..90609fa 100644
--- a/gdb/python/py-utils.c
+++ b/gdb/python/py-utils.c
@@ -19,6 +19,7 @@
 
 #include "defs.h"
 #include "charset.h"
+#include "value.h"
 #include "python-internal.h"
 
 
@@ -272,3 +273,51 @@ gdbpy_exception_to_string (PyObject *ptype, PyObject *pvalue)
 
   return str;
 }
+
+/* Converts OBJ to a CORE_ADDR value.
+
+   Returns 1 on success or 0 on failure, with a Python exception set.  This
+   function can also throw GDB exceptions.
+*/
+
+int
+get_addr_from_python (PyObject *obj, CORE_ADDR *addr)
+{
+  if (gdbpy_is_value_object (obj))
+    *addr = value_as_address (value_object_to_value (obj));
+  else if (PyLong_Check (obj))
+    {
+      /* Assume CORE_ADDR corresponds to unsigned long.  */
+      *addr = PyLong_AsUnsignedLong (obj);
+      if (PyErr_Occurred () != NULL)
+	return 0;
+    }
+  else if (PyInt_Check (obj))
+    {
+      long val;
+
+      /* Assume CORE_ADDR corresponds to unsigned long.  */
+      val = PyInt_AsLong (obj);
+
+      if (val >= 0)
+	*addr = val;
+      else
+      {
+	/* If no error ocurred, VAL is indeed negative.  */
+	if (PyErr_Occurred () != NULL)
+	  return 0;
+
+	PyErr_SetString (PyExc_ValueError,
+			 _("Supplied address is negative."));
+	return 0;
+      }
+    }
+  else
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       _("Invalid type for address."));
+      return 0;
+    }
+
+  return 1;
+}
diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c
index eb20de8..2024021 100644
--- a/gdb/python/py-value.c
+++ b/gdb/python/py-value.c
@@ -43,6 +43,10 @@
 /* Python's long type corresponds to C's long long type.  */
 #define builtin_type_pylong builtin_type (python_gdbarch)->builtin_long_long
 
+/* Python's long type corresponds to C's long long type.  Unsigned version.  */
+#define builtin_type_upylong builtin_type \
+  (python_gdbarch)->builtin_unsigned_long_long
+
 #define builtin_type_pybool \
   language_bool_type (python_language, python_gdbarch)
 
@@ -961,7 +965,34 @@ convert_value_from_python (PyObject *obj)
 	{
 	  LONGEST l = PyLong_AsLongLong (obj);
 
-	  if (! PyErr_Occurred ())
+	  if (PyErr_Occurred ())
+	    {
+	      /* If the error was an overflow, we can try converting to
+	         ULONGEST instead.  */
+	      if (PyErr_ExceptionMatches (PyExc_OverflowError))
+		{
+		  PyObject *etype, *evalue, *etraceback, *zero;
+
+		  PyErr_Fetch (&etype, &evalue, &etraceback);
+		  zero = PyInt_FromLong (0);
+
+		  /* Check whether obj is positive.  */
+		  if (PyObject_RichCompareBool (obj, zero, Py_GT) > 0)
+		    {
+		      ULONGEST ul;
+
+		      ul = PyLong_AsUnsignedLongLong (obj);
+		      if (! PyErr_Occurred ())
+			value = value_from_ulongest (builtin_type_upylong, ul);
+		    }
+		  else
+		    /* There's nothing we can do.  */
+		    PyErr_Restore (etype, evalue, etraceback);
+
+		  Py_DECREF (zero);
+		}
+	    }
+	  else
 	    value = value_from_longest (builtin_type_pylong, l);
 	}
       else if (PyFloat_Check (obj))
@@ -1028,6 +1059,14 @@ gdbpy_history (PyObject *self, PyObject *args)
   return value_to_value_object (res_val);
 }
 
+/* Returns 1 in OBJ is a gdb.Value object, 0 otherwise.  */
+
+int
+gdbpy_is_value_object (PyObject *obj)
+{
+  return PyObject_TypeCheck (obj, &value_object_type);
+}
+
 void
 gdbpy_initialize_values (void)
 {
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 5bd2a4b..daaed2c 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -80,6 +80,17 @@ extern PyTypeObject value_object_type;
 extern PyTypeObject block_object_type;
 extern PyTypeObject symbol_object_type;
 
+typedef struct
+{
+  PyObject_HEAD
+
+  /* The thread we represent.  */
+  struct thread_info *thread;
+
+  /* The Inferior object to which this thread belongs.  */
+  PyObject *inf_obj;
+} thread_object;
+
 extern struct cmd_list_element *set_python_list;
 extern struct cmd_list_element *show_python_list;
 
@@ -92,6 +103,8 @@ PyObject *gdbpy_block_for_pc (PyObject *self, PyObject *args);
 PyObject *gdbpy_lookup_type (PyObject *self, PyObject *args, PyObject *kw);
 PyObject *gdbpy_create_lazy_string_object (CORE_ADDR address, long length,
 					   const char *encoding, struct type *type);
+PyObject *gdbpy_inferiors (PyObject *unused, PyObject *unused2);
+PyObject *gdbpy_selected_thread (PyObject *self, PyObject *args);
 PyObject *gdbpy_string_to_argv (PyObject *self, PyObject *args);
 PyObject *gdbpy_get_hook_function (const char *);
 PyObject *gdbpy_parameter (PyObject *self, PyObject *args);
@@ -106,6 +119,7 @@ PyObject *symbol_to_symbol_object (struct symbol *sym);
 PyObject *block_to_block_object (struct block *block, struct objfile *objfile);
 PyObject *value_to_value_object (struct value *v);
 PyObject *type_to_type_object (struct type *);
+PyObject *frame_info_to_frame_object (struct frame_info *frame);
 
 PyObject *pspace_to_pspace_object (struct program_space *);
 PyObject *pspy_get_printers (PyObject *, void *);
@@ -113,6 +127,10 @@ PyObject *pspy_get_printers (PyObject *, void *);
 PyObject *objfile_to_objfile_object (struct objfile *);
 PyObject *objfpy_get_printers (PyObject *, void *);
 
+thread_object *create_thread_object (struct thread_info *tp);
+thread_object *find_thread_object (ptid_t ptid);
+PyObject *find_inferior_object (int pid);
+
 struct block *block_object_to_block (PyObject *obj);
 struct symbol *symbol_object_to_symbol (PyObject *obj);
 struct value *value_object_to_value (PyObject *self);
@@ -136,6 +154,8 @@ void gdbpy_initialize_objfile (void);
 void gdbpy_initialize_breakpoints (void);
 void gdbpy_initialize_lazy_string (void);
 void gdbpy_initialize_parameters (void);
+void gdbpy_initialize_thread (void);
+void gdbpy_initialize_inferior (void);
 
 struct cleanup *make_cleanup_py_decref (PyObject *py);
 
@@ -188,6 +208,8 @@ gdb_byte *gdbpy_extract_lazy_string (PyObject *string,
 				     struct type **str_type, 
 				     long *length, char **encoding);
 
+int gdbpy_is_value_object (PyObject *obj);
+
 /* Note that these are declared here, and not in python.h with the
    other pretty-printer functions, because they refer to PyObject.  */
 PyObject *apply_varobj_pretty_printer (PyObject *print_obj,
@@ -204,4 +226,6 @@ extern PyObject *gdbpy_enabled_cst;
 
 extern PyObject *gdbpy_gdberror_exc;
 
+int get_addr_from_python (PyObject *obj, CORE_ADDR *addr);
+
 #endif /* GDB_PYTHON_INTERNAL_H */
diff --git a/gdb/python/python.c b/gdb/python/python.c
index b9a8cf6..6680126 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -757,6 +757,8 @@ Enables or disables printing of Python stack traces."),
   gdbpy_initialize_objfile ();
   gdbpy_initialize_breakpoints ();
   gdbpy_initialize_lazy_string ();
+  gdbpy_initialize_thread ();
+  gdbpy_initialize_inferior ();
 
   PyRun_SimpleString ("import gdb");
   PyRun_SimpleString ("gdb.pretty_printers = []");
@@ -884,7 +886,12 @@ Arguments are separate by spaces and may be quoted."
     "Write a string using gdb's filtered stream." },
   { "flush", gdbpy_flush, METH_NOARGS,
     "Flush gdb's filtered stdout stream." },
-
+  { "selected_thread", gdbpy_selected_thread, METH_NOARGS,
+    "selected_thread () -> gdb.InferiorThread.\n\
+Return the selected thread object." },
+  { "inferiors", gdbpy_inferiors, METH_NOARGS,
+    "inferiors () -> (gdb.Inferior, ...).\n\
+Return a tuple containing all inferiors." },
   {NULL, NULL, 0, NULL}
 };
 
diff --git a/gdb/testsuite/gdb.python/Makefile.in b/gdb/testsuite/gdb.python/Makefile.in
index c6a2678..660941d 100644
--- a/gdb/testsuite/gdb.python/Makefile.in
+++ b/gdb/testsuite/gdb.python/Makefile.in
@@ -2,7 +2,7 @@ VPATH = @srcdir@
 srcdir = @srcdir@
 
 EXECUTABLES = py-type py-value py-prettyprint py-template py-block \
-		py-symbol py-mi py-breakpoint
+		py-symbol py-mi py-breakpoint py-inferior py-infthread
 
 all info install-info dvi install uninstall installcheck check:
 	@echo "Nothing to be done for $@..."
diff --git a/gdb/testsuite/gdb.python/py-inferior.c b/gdb/testsuite/gdb.python/py-inferior.c
new file mode 100644
index 0000000..0b48299
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-inferior.c
@@ -0,0 +1,49 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.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;
+
+
+int f2 (int a)
+{
+  char *str = "hello, testsuite";
+
+  puts (str);	/* Break here.  */
+
+  return ++a;
+}
+
+int f1 (int a, int b)
+{
+  return f2(a) + b;
+}
+
+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 (int argc, char *argv[])
+{
+  init_bufs ();
+
+  return f1 (1, 2);
+}
diff --git a/gdb/testsuite/gdb.python/py-inferior.exp b/gdb/testsuite/gdb.python/py-inferior.exp
new file mode 100644
index 0000000..309ae8c
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-inferior.exp
@@ -0,0 +1,191 @@
+# Copyright (C) 2009, 2010 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 file is part of the GDB testsuite.  It tests the mechanism
+# exposing inferiors to Python.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+# Run a command in GDB, and report a failure if a Python exception is thrown.
+# If report_pass is true, report a pass if no exception is thrown.
+proc gdb_py_test_silent_cmd {cmd name report_pass} {
+    global gdb_prompt
+
+  gdb_test_multiple $cmd $name {
+      -re "Traceback.*$gdb_prompt $"  { fail $name }
+      -re "$gdb_prompt $"	      { if $report_pass { pass $name } }
+  }
+}
+
+set testfile "py-inferior"
+set srcfile ${testfile}.c
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
+    return -1
+}
+
+# Start with a fresh gdb.
+clean_restart ${testfile}
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+# The following tests require execution.
+
+if ![runto_main] then {
+    fail "Can't run to main"
+    return 0
+}
+
+runto [gdb_get_line_number "Break here."]
+
+# Test basic gdb.Inferior attributes and methods.
+
+gdb_py_test_silent_cmd "python inferiors = gdb.inferiors ()" "get inferiors list" 1
+gdb_test "python print inferiors" "\\(<gdb.Inferior object at 0x\[\[:xdigit:\]\]+>,\\)" "verify inferiors list"
+gdb_py_test_silent_cmd "python i0 = inferiors\[0\]" "get first inferior" 0
+
+gdb_test "python print 'result =', i0 == inferiors\[0\]" " = True" "test equality comparison (true)"
+gdb_test "python print 'result =', i0.num" " = \[0-9\]+" "test Inferior.num"
+gdb_test "python print 'result =', i0.pid" " = \[0-9\]+" "test Inferior.pid"
+gdb_test "python print 'result =', i0.was_attached" " = False" "test Inferior.was_attached"
+gdb_test "python print i0.threads ()" "\\(<gdb.InferiorThread object at 0x\[\[:xdigit:\]\]+>,\\)" "test Inferior.threads"
+
+# Test memory read and write operations.
+
+gdb_py_test_silent_cmd "python addr = gdb.selected_frame ().read_var ('str')" \
+  "read str address" 0
+gdb_py_test_silent_cmd "python str = gdb.inferiors()\[0\].read_memory (addr, 5)" \
+  "read str contents" 1
+gdb_py_test_silent_cmd "python str\[1\] = 'a'" "change str" 0
+gdb_py_test_silent_cmd "python gdb.inferiors()\[0\].write_memory (addr, str)" \
+  "write str" 1
+gdb_test "print str" " = 0x\[\[:xdigit:\]\]+ \"hallo, testsuite\"" \
+  "ensure str was changed in the inferior"
+
+# Test memory search.
+
+set hex_number {0x[0-9a-fA-F][0-9a-fA-F]*}
+set dec_number {[0-9]+}
+set history_prefix {[$][0-9]* = }
+set newline {[\r\n]+}
+set pattern_not_found "${newline}.None"
+set one_pattern_found "${newline}.${dec_number}"
+
+# Test string pattern.
+
+gdb_test "set *(int32_t*) &int8_search_buf\[10\] = 0x61616161" "" ""
+gdb_test "py search_buf = gdb.selected_frame ().read_var ('int8_search_buf')" "" ""
+gdb_test "py start_addr = search_buf.address" "" ""
+gdb_test "py length = search_buf.type.sizeof" "" ""
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, 'aaa')" \
+  "${one_pattern_found}" "find string pattern"
+
+# Test not finding pattern because search range too small, with
+# potential find at the edge of the range.
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, 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 "py print gdb.inferiors()\[0\].search_memory (start_addr, 10+3+1, 'aaa')" \
+  "${one_pattern_found}" "pattern found at end of range"
+
+# Import struct to pack the following patterns.
+gdb_test "py from struct import *" "" ""
+
+# Test 16-bit pattern.
+
+gdb_test "set int16_search_buf\[10\] = 0x1234" "" ""
+gdb_test "py search_buf = gdb.selected_frame ().read_var ('int16_search_buf')" "" ""
+gdb_test "py start_addr = search_buf.address" "" ""
+gdb_test "py length = search_buf.type.sizeof" "" ""
+gdb_test "py pattern = pack('H',0x1234)" "" \
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, pattern)" \
+  "${one_pattern_found}" "find 16-bit pattern, with value pattern"
+
+# Test 32-bit pattern.
+
+gdb_test "set int32_search_buf\[10\] = 0x12345678" "" ""
+gdb_test "py search_buf = gdb.selected_frame ().read_var ('int32_search_buf')" "" ""
+gdb_test "py start_addr = search_buf.address" "" ""
+gdb_test "py length = search_buf.type.sizeof" "" ""
+gdb_test "py pattern = pack('I',0x12345678)" "" \
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, pattern)" \
+  "${one_pattern_found}" "find 32-bit pattern, with python pattern"
+
+# Test 64-bit pattern.
+
+gdb_test "set int64_search_buf\[10\] = 0xfedcba9876543210LL" "" ""
+gdb_test "py search_buf = gdb.selected_frame ().read_var ('int64_search_buf')" "" ""
+gdb_test "py start_addr = search_buf.address" "" ""
+gdb_test "py length = search_buf.type.sizeof" "" ""
+gdb_test "py pattern = pack('Q', 0xfedcba9876543210)" "" ""
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, pattern)" \
+  "${one_pattern_found}" "find 64-bit pattern, with value 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 "py search_buf = gdb.selected_frame ().read_var ('search_buf')" "" ""
+gdb_test "py start_addr = search_buf\[0\].address" "" ""
+gdb_test "py pattern1 = pack('B', 0x62)" "" ""
+gdb_test "py pattern2 = pack('H', 0x6363)" "" ""
+gdb_test "py pattern3 = pack('I', 0x64646464)" "" ""
+
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, 100, pattern1)" \
+    "${one_pattern_found}" "find mixed-sized pattern"
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, 100, pattern2)" \
+    "${one_pattern_found}" "find mixed-sized pattern"
+gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, 100, pattern3)" \
+    "${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 ;
+
+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 "py start_addr = gdb.selected_frame ().read_var ('search_buf')" "" ""
+gdb_test "py length = gdb.selected_frame ().read_var ('search_buf_size')" "" ""
+gdb_test "py pattern = pack('I', 0x12345678)" "" ""
+gdb_test "py first = gdb.inferiors()\[0\].search_memory (start_addr,length, pattern)" "" ""
+gdb_test "py print first" "${one_pattern_found}" "search spanning large range 1st result"
+gdb_test "py start_addr = first + 1"
+gdb_test "py second = gdb.inferiors()\[0\].search_memory (start_addr, length, pattern)" "" ""
+gdb_test "py print second" "${one_pattern_found}" "search spanning large range 2nd result"
+gdb_test "py start_addr = second + 1"
+gdb_test "py third = gdb.inferiors()\[0\].search_memory (start_addr, length, pattern)" "" ""
+gdb_test "py print third" "${pattern_not_found}" "search spanning large range 3rd result"
+
+# 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 "py pattern = pack('I', 0xfdb97531)" "" ""
+    gdb_test "py start_addr = gdb.selected_frame ().read_var ('search_buf')" "" ""
+    gdb_test "py print gdb.inferiors()\[0\].search_memory (start_addr, length, pattern)" \
+      "${one_pattern_found}" "find pattern straddling chunk boundary"
+}
diff --git a/gdb/testsuite/gdb.python/py-infthread.c b/gdb/testsuite/gdb.python/py-infthread.c
new file mode 100644
index 0000000..22eb9f2
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-infthread.c
@@ -0,0 +1,14 @@
+int f2 (int a)
+{
+  return ++a;
+}
+
+int f1 (int a, int b)
+{
+  return f2(a) + b;
+}
+
+int main (int argc, char *argv[])
+{
+  return f1 (1, 2);
+}
diff --git a/gdb/testsuite/gdb.python/py-infthread.exp b/gdb/testsuite/gdb.python/py-infthread.exp
new file mode 100644
index 0000000..a25d0b5
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-infthread.exp
@@ -0,0 +1,53 @@
+# Copyright (C) 2009, 2010 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 file is part of the GDB testsuite.  It tests the mechanism
+# exposing inferior threads to Python.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+set testfile "py-infthread"
+set srcfile ${testfile}.c
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
+    return -1
+}
+
+# Start with a fresh gdb.
+clean_restart ${testfile}
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+# The following tests require execution.
+
+if ![runto_main] then {
+    fail "Can't run to main"
+    return 0
+}
+
+runto [gdb_get_line_number "Break here."]
+
+# Test basic gdb.Inferior attributes and methods.
+
+gdb_py_test_silent_cmd "python t0 = gdb.selected_thread ()" "test gdb.selected_thread" 1
+gdb_test "python print t0" "\\<gdb.InferiorThread object at 0x\[\[:xdigit:\]\]+>" "verify InferiorThread object"
+gdb_test "python print 'result =', t0.num" " = \[0-9\]+" "test Inferior.num"
+gdb_test "python print 'result =', t0.ptid" " = \\(\[0-9\]+, \[0-9\]+, \[0-9\]+\\)" "test InferiorThread.ptid"
+
+gdb_test "python print 'result =', t0.is_stopped ()" " = True" "test InferiorThread.is_stopped"
+gdb_test "python print 'result =', t0.is_running ()" " = False" "test InferiorThread.is_running"
+gdb_test "python print 'result =', t0.is_exited ()" " = False" "test InferiorThread.is_exited"
diff --git a/gdb/value.c b/gdb/value.c
index 1c0bc57..64ac563 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -2250,6 +2250,43 @@ pack_long (gdb_byte *buf, struct type *type, LONGEST num)
 }
 
 
+/* Pack NUM into BUF using a target format of TYPE.  */
+
+void
+pack_unsigned_long (gdb_byte *buf, struct type *type, ULONGEST num)
+{
+  int len;
+  enum bfd_endian byte_order;
+
+  type = check_typedef (type);
+  len = TYPE_LENGTH (type);
+  byte_order = gdbarch_byte_order (get_type_arch (type));
+
+  switch (TYPE_CODE (type))
+    {
+    case TYPE_CODE_INT:
+    case TYPE_CODE_CHAR:
+    case TYPE_CODE_ENUM:
+    case TYPE_CODE_FLAGS:
+    case TYPE_CODE_BOOL:
+    case TYPE_CODE_RANGE:
+    case TYPE_CODE_MEMBERPTR:
+      store_unsigned_integer (buf, len, byte_order, num);
+      break;
+
+    case TYPE_CODE_REF:
+    case TYPE_CODE_PTR:
+      store_typed_address (buf, type, (CORE_ADDR) num);
+      break;
+
+    default:
+      error (_("\
+Unexpected type (%d) encountered for unsigned integer constant."),
+	     TYPE_CODE (type));
+    }
+}
+
+
 /* Convert C numbers into newly allocated values.  */
 
 struct value *
@@ -2262,6 +2299,19 @@ value_from_longest (struct type *type, LONGEST num)
 }
 
 
+/* Convert C unsigned numbers into newly allocated values.  */
+
+struct value *
+value_from_ulongest (struct type *type, ULONGEST num)
+{
+  struct value *val = allocate_value (type);
+
+  pack_unsigned_long (value_contents_raw (val), type, num);
+
+  return val;
+}
+
+
 /* Create a value representing a pointer of type TYPE to the address
    ADDR.  */
 struct value *
diff --git a/gdb/value.h b/gdb/value.h
index c3dfb17..474e661 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -366,6 +366,7 @@ extern LONGEST unpack_field_as_long (struct type *type,
 extern void pack_long (gdb_byte *buf, struct type *type, LONGEST num);
 
 extern struct value *value_from_longest (struct type *type, LONGEST num);
+extern struct value *value_from_ulongest (struct type *type, ULONGEST num);
 extern struct value *value_from_pointer (struct type *type, CORE_ADDR addr);
 extern struct value *value_from_double (struct type *type, DOUBLEST num);
 extern struct value *value_from_decfloat (struct type *type,



^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [python][patch] Inferior and Thread information support.
  2010-06-28  9:22           ` Phil Muldoon
@ 2010-06-28 19:51             ` Tom Tromey
  2010-06-28 21:35               ` Phil Muldoon
  0 siblings, 1 reply; 21+ messages in thread
From: Tom Tromey @ 2010-06-28 19:51 UTC (permalink / raw)
  To: Phil Muldoon; +Cc: gdb-patches ml

>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:

Phil> +  cleanup = ensure_python_env (python_gdbarch, python_language);
Phil> +
Phil> +  thread_obj = create_thread_object (tp);
Phil> +  if (!thread_obj)
Phil> +    {
Phil> +      warning (_("Cannot create Python InferiorThread object."));
Phil> +      gdbpy_print_stack ();
Phil> +      do_cleanups (cleanup);

Tom> I don't think there is any need for a warning here.
Tom> The exception should suffice.

Phil> OK
 
Actually I meant that you should remove the call to warning but leave in
the gdbpy_print_stack.

Phil> +      buffer = xmalloc (length);
Phil> +      make_cleanup (xfree, buffer);
Phil> +
Phil> +      read_memory (addr, buffer, length);
Phil> +    }
Phil> +  GDB_PY_HANDLE_EXCEPTION (except);

Tom> GDB_PY_HANDLE_EXCEPTION returns from the function; this will leave the
Tom> cleanups dangling.  You have to run the cleanups before the return.

Phil> OK

The new code looks like this:

Phil> +  if (except.reason < 0)
Phil> +    {
Phil> +      do_cleanups (cleanups);
Phil> +      return PyErr_Format (PyExc_RuntimeError, "%s", except.message);
Phil> +    }

... but this does the wrong thing for RETURN_QUIT.
Instead I think you should do

if (except.reason < 0)
  {
    do_cleanups ...;
    GDB_PY_HANDLE_EXCEPTION (...);
  }

This is a little redundant but it will do the right thing -- and will
continue to do so if we ever split the different kinds of gdb
exceptions into different Python exceptions.

This patch is ok with those changes.  Thanks for persevering.

Tom

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [python][patch] Inferior and Thread information support.
  2010-06-28 19:51             ` Tom Tromey
@ 2010-06-28 21:35               ` Phil Muldoon
  0 siblings, 0 replies; 21+ messages in thread
From: Phil Muldoon @ 2010-06-28 21:35 UTC (permalink / raw)
  To: tromey; +Cc: gdb-patches ml

On 28/06/10 20:51, Tom Tromey wrote:
>>>>>> "Phil" == Phil Muldoon <pmuldoon@redhat.com> writes:
> Phil> +  if (!thread_obj)
> Phil> +    {
> Phil> +      warning (_("Cannot create Python InferiorThread object."));
> Phil> +      gdbpy_print_stack ();
> Phil> +      do_cleanups (cleanup);
> 
> Tom> I don't think there is any need for a warning here.
> Tom> The exception should suffice.
> 
> Phil> OK
>  
> Actually I meant that you should remove the call to warning but leave in
> the gdbpy_print_stack.


Ok, thanks.


> The new code looks like this:
> 
> Phil> +  if (except.reason < 0)
> Phil> +    {
> Phil> +      do_cleanups (cleanups);
> Phil> +      return PyErr_Format (PyExc_RuntimeError, "%s", except.message);
> Phil> +    }
> 
> ... but this does the wrong thing for RETURN_QUIT.
> Instead I think you should do
> 
> if (except.reason < 0)
>   {
>     do_cleanups ...;
>     GDB_PY_HANDLE_EXCEPTION (...);
>   }
> 
> This is a little redundant but it will do the right thing -- and will
> continue to do so if we ever split the different kinds of gdb
> exceptions into different Python exceptions.


Ok, makes sense.


> This patch is ok with those changes.  Thanks for persevering.


And so committed with those changes.

http://sourceware.org/ml/gdb-cvs/2010-06/msg00200.html

And thank-you and all the other commentators for their guidance on
this Python API.  This is the last major Python API patch from
Archer. It's been fun getting these features into FSF GDB.

Cheers,

Phil

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2010-06-28 21:35 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-05-24 13:36 [python][patch] Inferior and Thread information support Phil Muldoon
2010-05-24 18:06 ` Eli Zaretskii
2010-06-10 18:40 ` Tom Tromey
2010-06-14 12:42   ` Phil Muldoon
2010-06-15 15:24     ` Pedro Alves
2010-06-15 18:11     ` Tom Tromey
2010-06-15 18:24       ` Pedro Alves
2010-06-15 19:58       ` Phil Muldoon
2010-06-15 20:36         ` Pedro Alves
2010-06-18  6:49   ` Phil Muldoon
2010-06-18 14:21     ` Doug Evans
2010-06-18 15:47       ` Phil Muldoon
2010-06-18 17:59         ` Tom Tromey
2010-06-18 20:10           ` Phil Muldoon
2010-06-25 20:41             ` Tom Tromey
2010-06-18 18:04     ` Tom Tromey
2010-06-22 10:32       ` Phil Muldoon
2010-06-25 20:38         ` Tom Tromey
2010-06-28  9:22           ` Phil Muldoon
2010-06-28 19:51             ` Tom Tromey
2010-06-28 21:35               ` Phil Muldoon

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).