This is the mail archive of the archer@sourceware.org mailing list for the Archer project.


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

[python][commit] Add interface for inferior manipulation.


Hi,

This patch is the beginning of the implementation of the inferior and
threads API suggested by Tromey in:

http://sourceware.org/ml/archer/2008-q4/msg00430.html

I won't work on much more than what's here, though. I will only move a
few functions and methods to the Inferior and InferiorThread classes if
it makes sense. In particular, I won't work on inferior events.

The InferiorThread class is very basic (and probably buggy), so I intend
to add a few more attributes and perhaps methods to it, and create a
real testcase.

One thing worth mentioning is that for each inferior and thread that
gets created, this code will always create a corresponding Python
object, even if no Python script is ever run by the GDB instance. This
adds a (hopefully)) small performance and time penalty to the GDB
session.

I intend to alleviate this problem by only starting to track inferiors
and threads after the first thread or inferior Python object needs to be
created. After that, though, all threads and inferiors will get a
corresponding Python object.

I coded it this way because it seemed important/useful to always have
only one Python object for each GDB inferior or thread. Perhaps I'll
revisit this though, and create Python objects on demand (thus
permitting more than one Python instance for each inferior or thread).
Or change the code to use a hashtable instead. This is the direction
Tromey took in an early code of his, but I decided against it for
simplicity.

Committed to the Python branch. I intend to post this patch upstream
(after fixing the things I mentioned) by the end of the week, so I'd
appreciate feedback if you have any.
-- 
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center


gdb/
	* Makefile.in (SUBDIR_PYTHON_OBS): Add python-inferior.o and
	python-infthread.o.
	(SUBDIR_PYTHON_SRCS): Add python-inferior.c and
	python-infthread.c.
	(python-inferior.o): New target.
	(python-infthread.o): New target.
	* python/python-inferior.c: New file.
	* python/python-infthread.c: New file.
	* python/python-frame.c (frame_info_to_frame_object): Remove
	static keyword, return pointer to PyObject.  Adapt callers.
	(gdbpy_frames): Move to python-infthread.c, as thpy_frames.
	(gdbpy_newest_frames: Move to python-infthread.c, as
	`thpy_newest_frame'.
	* python/python-internal.h (thread_object): New typedef.
	(gdbpy_frames, gdbpy_newest_frame): Remove function prototypes.
	 (gdbpy_inferiors, gdbpy_selected_thread,
	 frame_info_to_frame_object, create_thread_object,
	 find_thread_object, find_inferior_object,
	 gdbpy_initialize_thread, gdbpy_initialize_inferior): New function
	 prototypes.
	* python/python.c (count_callback, update_tuple_callback):
	* Remove functions.
	(gdbpy_threads): Move to python-inferior.c, as infpy_threads.
	(gdbpy_current_thread): Remove function.
	(gdbpy_switch_to_thread): Remove function.
	(_initialize_python): Call gdbpy_initialize_thread and
	gdbpy_initialize_inferior.
	(GdbMethods): Remove `frames', `newest_frame', `threads',
	`current_thread' and `switch_to_thread' entries. Add
	`selected_thread' and `inferiors' entries.
	* python/lib/gdb/command/backtrace.py: Adapt to new thread API.

gdb/doc/
	* gdb.texinfo (Inferiors In Python): New node.
	(Python API): Add `Inferiors In Python' node.
	(Threads In Python): Rewrite to adapt to new thread API.
	(Frames In Python): Remove `frames' and `newest_frame'
	functions.

gdb/testsuite/
	* gdb.python/python-frame.exp: Adapt to new thread API.
	* gdb.python/python-inferior.exp: New file.
	* gdb.python/python-inferior.c: New file.
	* gdb.python/python-infthread.exp: New file.
	* gdb.python/python-infthread.c: New file.

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index f32bb1d..514506c 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -283,6 +283,8 @@ SUBDIR_PYTHON_OBS = \
 	python-frame.o \
 	python-function.o \
 	python-hooks.o \
+	python-inferior.o \
+	python-infthread.o \
 	python-membuf.o \
 	python-objfile.o \
 	python-param.o \
@@ -299,6 +301,8 @@ SUBDIR_PYTHON_SRCS = \
 	python/python-frame.c \
 	python/python-function.c \
 	python/python-hooks.c \
+	python/python-inferior.c \
+	python/python-infthread.c \
 	python/python-membuf.c \
 	python/python-objfile.c \
 	python/python-param.c \
@@ -1897,6 +1901,14 @@ python-hooks.o: $(srcdir)/python/python-hooks.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-hooks.c
 	$(POSTCOMPILE)
 
+python-inferior.o: $(srcdir)/python/python-inferior.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-inferior.c
+	$(POSTCOMPILE)
+
+python-infthread.o: $(srcdir)/python/python-infthread.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-infthread.c
+	$(POSTCOMPILE)
+
 python-membuf.o: $(srcdir)/python/python-membuf.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-membuf.c
 	$(POSTCOMPILE)
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 8509ecc..4ec2cc9 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -18137,6 +18137,7 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
 * Values From Inferior::	Python representation of values.
 * Types In Python::		Python representation of types.
 * Pretty Printing::		Pretty-printing values.
+* 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.
@@ -18900,33 +18901,87 @@ 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}).  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, assigned by the underlying OS.
+@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
+@end table
+
 @node Threads In Python
 @subsubsection Threads In Python
 
-Python scripts can access information about the inferior's threads
-using some functions provided by @value{GDBN}.  Like all of
-@value{GDBN}'s Python additions, these are in the @code{gdb} module:
+Python scripts can access information about and manipulate inferior threads
+controlled by @value{GDBN} via objects of the @code{gdb.InferiorThread} class.
 
-@findex gdb.threads
-@defun threads
-This function returns a tuple holding all the thread IDs which are
-valid when the function is called.  If there are no valid threads,
-this will return @code{None}.
-@end defun
+The following inferior-related functions are available in the @code{gdb}
+module:
 
-@findex gdb.current_thread
-@defun current_thread
-This function returns the thread ID of the selected thread.  If there
+@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
 
-@findex gdb.switch_to_thread
-@defun switch_to_thread id
-This changes @value{GDBN}'s currently selected thread to the thread
-given by @var{id}.  @var{id} must be a valid thread ID as returned by
-@code{threads}.  If @var{id} is invalid, this function throws an
-exception.
-@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
+@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_to_thread 
+This changes @value{GDBN}'s currently selected thread to the one represented
+by this object.
+@end defmethod
+@end table
 
 @node Commands In Python
 @subsubsection Commands In Python
@@ -19496,14 +19551,6 @@ True
 
 The following frame-related functions are available in the @code{gdb} module:
 
-@defun frames
-Return a tuple containing all frame objects.
-@end defun
-
-@defun newest_frame
-Return the newest frame object.
-@end defun
-
 @defun selected_frame
 Return the selected frame object.  (@pxref{Selection,,Selecting a Frame}).
 @end defun
diff --git a/gdb/python/lib/gdb/command/backtrace.py b/gdb/python/lib/gdb/command/backtrace.py
index 17b1c18..689b94b 100644
--- a/gdb/python/lib/gdb/command/backtrace.py
+++ b/gdb/python/lib/gdb/command/backtrace.py
@@ -1,6 +1,6 @@
 # New backtrace command.
 
-# Copyright (C) 2008 Free Software Foundation, Inc.
+# Copyright (C) 2008, 2009 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
@@ -172,8 +172,9 @@ Use of the 'raw' qualifier avoids any filtering by loadable modules.
 
         # FIXME: provide option to start at selected frame
         # However, should still number as if starting from newest
+	newest_frame = gdb.selected_thread ().newest_frame ()
         iter = itertools.imap (FrameWrapper,
-                               FrameIterator (gdb.newest_frame ()))
+                               FrameIterator (newest_frame)
         if filter:
             iter = gdb.backtrace.create_frame_filter (iter)
 
diff --git a/gdb/python/python-frame.c b/gdb/python/python-frame.c
index 53f29d4..ad03c46 100644
--- a/gdb/python/python-frame.c
+++ b/gdb/python/python-frame.c
@@ -255,7 +255,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;
@@ -285,7 +285,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.
@@ -305,7 +305,7 @@ frapy_older (PyObject *self, PyObject *args)
 
       prev = get_prev_frame (frame);
       if (prev)
-	prev_obj = (PyObject *) frame_info_to_frame_object (prev);
+	prev_obj = frame_info_to_frame_object (prev);
       else
 	{
 	  Py_INCREF (Py_None);
@@ -334,7 +334,7 @@ frapy_newer (PyObject *self, PyObject *args)
 
       next = get_next_frame (frame);
       if (next)
-	next_obj = (PyObject *) frame_info_to_frame_object (next);
+	next_obj = frame_info_to_frame_object (next);
       else
 	{
 	  Py_INCREF (Py_None);
@@ -441,79 +441,6 @@ frapy_read_var (PyObject *self, PyObject *args)
   Py_RETURN_NONE;
 }
 
-/* Implementation of gdb.frames () -> (gdb.Frame, ...).
-   Returns a tuple of all frame objects.  */
-
-PyObject *
-gdbpy_frames (PyObject *self, PyObject *args)
-{
-  int result = 0;
-  struct frame_info *frame;
-  frame_object *frame_obj;
-  PyObject *list, *tuple;
-  volatile struct gdb_exception except;
-
-  list = PyList_New (0);
-  if (list == NULL)
-    {
-      PyErr_SetString (PyExc_MemoryError, "Could not allocate frames list.");
-      return NULL;
-    }
-
-  TRY_CATCH (except, RETURN_MASK_ALL)
-    {
-      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, (PyObject *) frame_obj);
-	}
-    }
-  if (except.reason < 0)
-    {
-      Py_DECREF (list);
-      return PyErr_Format (except.reason == RETURN_QUIT
-			   ? PyExc_KeyboardInterrupt : PyExc_RuntimeError,
-			   "%s", except.message);
-    }
-
-  if (list)
-    {
-      tuple = PyList_AsTuple (list);
-      Py_DECREF (list);
-    }
-  else
-    tuple = NULL;
-
-  return tuple;
-}
-
-/* Implementation of gdb.newest_frame () -> gdb.Frame.
-   Returns the newest frame object.  */
-
-PyObject *
-gdbpy_newest_frame (PyObject *self, PyObject *args)
-{
-  struct frame_info *frame;
-  frame_object *frame_obj = NULL;   /* Initialize to appease gcc warning.  */
-  volatile struct gdb_exception except;
-
-  TRY_CATCH (except, RETURN_MASK_ALL)
-    {
-      frame = get_current_frame ();
-      frame_obj = frame_info_to_frame_object (frame);
-    }
-  GDB_PY_HANDLE_EXCEPTION (except);
-
-  return (PyObject *) frame_obj;
-}
-
 /* Implementation of gdb.selected_frame () -> gdb.Frame.
    Returns the selected frame object.  */
 
@@ -521,7 +448,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)
@@ -531,7 +458,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/python-inferior.c b/gdb/python/python-inferior.c
new file mode 100644
index 0000000..7842874
--- /dev/null
+++ b/gdb/python/python-inferior.c
@@ -0,0 +1,432 @@
+/* Python interface to inferiors.
+
+   Copyright (C) 2009 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 "gdbthread.h"
+#include "inferior.h"
+#include "observer.h"
+#include "python-internal.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;
+
+/* 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;
+};
+
+
+
+/* Inferior objects list.  */
+
+/* List containing inferior_objects.  This list owns a reference to each
+   object it contains.  */
+static struct inflist_entry *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 (int pid)
+{
+  struct inferior *inf = find_inferior_pid (pid);
+  inferior_object *inf_obj;
+  struct inflist_entry *entry;
+  PyGILState_STATE state;
+  struct cleanup *cleanup;
+
+  if (!inf)
+    {
+      warning (_("Can't create Python Inferior object."));
+      return;
+    }
+
+  state = PyGILState_Ensure ();
+  cleanup = make_cleanup_py_restore_gil (&state);
+
+  inf_obj = PyObject_New (inferior_object, &inferior_object_type);
+  if (!inf_obj)
+    {
+      warning (_("Can't 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 = inferior_list;
+
+  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 (int pid)
+{
+  PyGILState_STATE state;
+  struct inflist_entry **inf_entry, *inf_tmp;
+  struct threadlist_entry *th_entry, *th_tmp;
+
+  /* Find inferior_object for the given PID.  */
+  for (inf_entry = &inferior_list; *inf_entry != NULL;
+       inf_entry = &(*inf_entry)->next)
+    if ((*inf_entry)->inf_obj->inferior->pid == pid)
+      break;
+
+  if (!*inf_entry)
+    return;
+
+  state = PyGILState_Ensure ();
+
+  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--;
+
+  PyGILState_Release (state);
+}
+
+/* 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 = 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 = 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;
+}
+
+
+
+/* Inferior object.  */
+
+static void
+add_thread_object (struct thread_info *tp)
+{
+  PyGILState_STATE state;
+  thread_object *thread_obj;
+  inferior_object *inf_obj;
+  struct threadlist_entry *entry;
+
+  state = PyGILState_Ensure ();
+
+  thread_obj = create_thread_object (tp);
+  if (!thread_obj)
+    {
+      warning (_("Can't create Python InferiorThread object."));
+      gdbpy_print_stack ();
+      PyGILState_Release (state);
+      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++;
+
+  PyGILState_Release (state);
+}
+
+static void
+delete_thread_object (struct thread_info *tp)
+{
+  PyGILState_STATE state;
+  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;
+
+  state = PyGILState_Ensure ();
+
+  tmp = *entry;
+  tmp->thread_obj->thread = NULL;
+
+  *entry = (*entry)->next;
+  inf_obj->nthreads--;
+
+  Py_DECREF (tmp->thread_obj);
+  xfree (tmp);
+
+
+  PyGILState_Release (state);
+}
+
+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),
+     is this a problem?  */
+  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;
+}
+
+
+
+/* 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),
+     is this a problem?  */
+  for (i = 0, entry = inferior_list; i < ninferiors; i++, entry = entry->next)
+    {
+      Py_INCREF (entry->inf_obj);
+      PyTuple_SET_ITEM (tuple, i, (PyObject *) entry->inf_obj);
+    }
+
+  return tuple;
+}
+
+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);
+
+  inferior_list = NULL;
+  ninferiors = 0;
+
+  observer_attach_new_inferior (add_inferior_object);
+  observer_attach_inferior_exit (delete_inferior_object);
+  observer_attach_new_thread (add_thread_object);
+  observer_attach_thread_exit (delete_thread_object);
+}
+
+
+
+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." },
+
+  { 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 */
+};
diff --git a/gdb/python/python-infthread.c b/gdb/python/python-infthread.c
new file mode 100644
index 0000000..21e4eab
--- /dev/null
+++ b/gdb/python/python-infthread.c
@@ -0,0 +1,285 @@
+/* Python interface to inferior threads.
+
+   Copyright (C) 2009 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);
+}
+
+
+
+/* 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 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 },
+
+  { 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." },
+
+  { 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/python-internal.h b/gdb/python/python-internal.h
index 5e43267..afb0c51 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -73,10 +73,20 @@ extern PyTypeObject block_object_type;
 extern PyTypeObject value_object_type;
 extern PyTypeObject symbol_object_type;
 
+/* Used in python-inferior.c.  */
+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;
+
 PyObject *gdbpy_history (PyObject *self, PyObject *args);
 PyObject *gdbpy_breakpoints (PyObject *, PyObject *);
-PyObject *gdbpy_frames (PyObject *, PyObject *);
-PyObject *gdbpy_newest_frame (PyObject *, PyObject *);
 PyObject *gdbpy_frame_stop_reason_string (PyObject *, PyObject *);
 PyObject *gdbpy_lookup_symbol (PyObject *self, PyObject *args, PyObject *kw);
 PyObject *gdbpy_selected_frame (PyObject *self, PyObject *args);
@@ -84,6 +94,8 @@ PyObject *gdbpy_block_for_pc (PyObject *self, PyObject *args);
 PyObject *gdbpy_read_memory (PyObject *self, PyObject *args);
 PyObject *gdbpy_write_memory (PyObject *self, PyObject *args);
 PyObject *gdbpy_lookup_type (PyObject *self, PyObject *args, PyObject *kw);
+PyObject *gdbpy_inferiors (PyObject *unused, PyObject *unused2);
+PyObject *gdbpy_selected_thread (PyObject *self, PyObject *args);
 
 PyObject *symtab_and_line_to_sal_object (struct symtab_and_line sal);
 PyObject *symtab_to_symtab_object (struct symtab *symtab);
@@ -92,6 +104,10 @@ PyObject *block_to_block_object (struct block *block);
 PyObject *value_to_value_object (struct value *v);
 PyObject *type_to_type_object (struct type *);
 PyObject *objfile_to_objfile_object (struct objfile *);
+PyObject *frame_info_to_frame_object (struct frame_info *frame);
+thread_object *create_thread_object (struct thread_info *tp);
+thread_object *find_thread_object (ptid_t ptid);
+PyObject *find_inferior_object (int pid);
 
 PyObject *objfpy_get_printers (PyObject *, void *);
 
@@ -114,6 +130,8 @@ void gdbpy_initialize_blocks (void);
 void gdbpy_initialize_functions (void);
 void gdbpy_initialize_objfile (void);
 void gdbpy_initialize_parameters (void);
+void gdbpy_initialize_thread (void);
+void gdbpy_initialize_inferior (void);
 void gdbpy_initialize_membuf (void);
 
 struct cleanup *make_cleanup_py_decref (PyObject *py);
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 2d72f13..eddf7bf 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -744,89 +744,6 @@ gdbpy_initialize_events (void)
 
 
 
-/* Threads.  */
-
-/* Callback function for use with iterate_over_threads.  This function
-   just counts the number of threads.  */
-
-static int
-count_callback (struct thread_info *info, void *user_data)
-{
-  int *count = (int *) user_data;
-  ++*count;
-  return 0;
-}
-
-/* Structure for storing some state when iterating over threads.  */
-
-struct set_thread_info
-{
-  PyObject *tuple;
-  int index;
-};
-
-/* Callback function for use with iterate_over_threads.  This function
-   stores the thread ID into a Python tuple.  */
-
-static int
-update_tuple_callback (struct thread_info *info, void *user_data)
-{
-  struct set_thread_info *tinfo = (struct set_thread_info *) user_data;
-  PyTuple_SetItem (tinfo->tuple, tinfo->index, PyInt_FromLong (info->num));
-  ++tinfo->index;
-  return 0;
-}
-
-/* Python function which yields a tuple holding all valid thread IDs.  */
-
-static PyObject *
-gdbpy_threads (PyObject *unused1, PyObject *unused2)
-{
-  int thread_count = 0;
-  struct set_thread_info info;
-  PyObject *result;
-
-  prune_threads ();
-  target_find_new_threads ();
-
-  iterate_over_threads (count_callback, &thread_count);
-
-  if (!thread_count)
-    Py_RETURN_NONE;
-
-  result = PyTuple_New (thread_count);
-  info.tuple = result;
-  info.index = 0;
-  iterate_over_threads (update_tuple_callback, &info);
-  return result;
-}
-
-/* Python function that returns the current thread's ID.  */
-
-static PyObject *
-gdbpy_current_thread (PyObject *unused1, PyObject *unused2)
-{
-  if (PIDGET (inferior_ptid) == 0)
-    Py_RETURN_NONE;
-  return PyInt_FromLong (pid_to_thread_id (inferior_ptid));
-}
-
-/* Python function for switching to a given thread.  */
-
-static PyObject *
-gdbpy_switch_to_thread (PyObject *self, PyObject *args)
-{
-  int id;
-  if (! PyArg_ParseTuple (args, "i", &id))
-    return NULL;
-  if (! valid_thread_id (id))
-    return PyErr_Format (PyExc_RuntimeError, "invalid thread id");
-  switch_to_thread (thread_id_to_pid (id));
-  Py_RETURN_NONE;
-}
-
-
-
 /* Printing.  */
 
 /* A python function to write a single string using gdb's filtered
@@ -1767,6 +1684,8 @@ Enables or disables auto-loading of Python code when an object is opened."),
   gdbpy_initialize_types ();
   gdbpy_initialize_parameters ();
   gdbpy_initialize_objfile ();
+  gdbpy_initialize_thread ();
+  gdbpy_initialize_inferior ();
   gdbpy_initialize_events ();
   gdbpy_initialize_membuf ();
 
@@ -1848,12 +1767,6 @@ static PyMethodDef GdbMethods[] =
   { "objfiles", gdbpy_objfiles, METH_NOARGS,
     "Return a sequence of all loaded objfiles." },
 
-  { "frames", gdbpy_frames, METH_NOARGS,
-    "frames () -> (gdb.Frame, ...).\n\
-Return a tuple containing all frame objects." },
-  { "newest_frame", gdbpy_newest_frame, METH_NOARGS,
-    "newest_frame () -> gdb.Frame.\n\
-Return the newest frame object." },
   { "selected_frame", gdbpy_selected_frame, METH_NOARGS,
     "selected_frame () -> gdb.Frame.\n\
 Return the selected frame object." },
@@ -1884,12 +1797,12 @@ Return the name of the shared library holding a given address, or None." },
 Return a tuple holding the file name (or None) and line number (or None).\n\
 Note: may later change to return an object." },
 
-  { "threads", gdbpy_threads, METH_NOARGS,
-    "Return a tuple holding all the valid thread IDs." },
-  { "current_thread", gdbpy_current_thread, METH_NOARGS,
-    "Return the thread ID of the current thread." },
-  { "switch_to_thread", gdbpy_switch_to_thread, METH_VARARGS,
-    "Switch to a thread, given the thread ID." },
+  { "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." },
 
   { "parse_and_eval", gdbpy_parse_and_eval, METH_VARARGS,
     "Parse a string as an expression, evaluate it, and return the result." },
diff --git a/gdb/testsuite/gdb.python/python-frame.exp b/gdb/testsuite/gdb.python/python-frame.exp
index cbf1095..c17411f 100644
--- a/gdb/testsuite/gdb.python/python-frame.exp
+++ b/gdb/testsuite/gdb.python/python-frame.exp
@@ -65,7 +65,7 @@ gdb_breakpoint "f2"
 gdb_continue_to_breakpoint "breakpoint at f2"
 gdb_test "up" "" ""
 
-gdb_py_test_silent_cmd "python frames = gdb.frames ()" "get frames list" 1
+gdb_py_test_silent_cmd "python frames = gdb.selected_thread ().frames ()" "get frames list" 1
 gdb_test "python print frames" "\\(<gdb.Frame object at 0x\[\[:xdigit:\]\]+>, <gdb.Frame object at 0x\[\[:xdigit:\]\]+>, <gdb.Frame object at 0x\[\[:xdigit:\]\]+>\\)" "verify frames list"
 gdb_py_test_silent_cmd "python f0 = frames\[0\]" "get first frame" 0
 gdb_py_test_silent_cmd "python f1 = frames\[1\]" "get second frame" 0
@@ -86,7 +86,7 @@ gdb_test "python print 'result =', f0.read_var ('variable_which_surely_doesnt_ex
   "test Frame.read_var - error"
 gdb_test "python print 'result =', f0.read_var ('a')" " = 1" "test Frame.read_var - success"
 
-gdb_test "python print 'result =', gdb.newest_frame () == f0" " = True" "test gdb.newest_frame"
+gdb_test "python print 'result =', gdb.selected_thread ().newest_frame () == f0" " = True" "test gdb.newest_frame"
 gdb_test "python print 'result =', gdb.selected_frame () == f1" " = True" "test gdb.selected_frame"
 
 gdb_test "python print 'result =', f0.block ()" "<gdb.Block object at 0x\[\[:xdigit:\]\]+>" "test Frame.block"
diff --git a/gdb/testsuite/gdb.python/python-inferior.c b/gdb/testsuite/gdb.python/python-inferior.c
new file mode 100644
index 0000000..22eb9f2
--- /dev/null
+++ b/gdb/testsuite/gdb.python/python-inferior.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/python-inferior.exp b/gdb/testsuite/gdb.python/python-inferior.exp
new file mode 100644
index 0000000..e99aba4
--- /dev/null
+++ b/gdb/testsuite/gdb.python/python-inferior.exp
@@ -0,0 +1,76 @@
+# Copyright (C) 2009 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
+}
+
+set testfile "python-inferior"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    untested "Couldn't compile ${srcfile}"
+    return -1
+}
+
+# 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 } }
+  }
+}
+
+# Start with a fresh gdb.
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+gdb_test_multiple "python print 'hello, world!'" "verify python support" {
+    -re "not supported.*$gdb_prompt $"	{
+      unsupported "python support is disabled"
+      return -1
+    }
+    -re "$gdb_prompt $"	{}
+}
+
+# The following tests require execution.
+
+if ![runto_main] then {
+    fail "Can't run to main"
+    return 0
+}
+
+gdb_breakpoint "f2"
+gdb_continue_to_breakpoint "breakpoint at f2"
+gdb_test "up" "" ""
+
+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"
diff --git a/gdb/testsuite/gdb.python/python-infthread.c b/gdb/testsuite/gdb.python/python-infthread.c
new file mode 100644
index 0000000..22eb9f2
--- /dev/null
+++ b/gdb/testsuite/gdb.python/python-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/python-infthread.exp b/gdb/testsuite/gdb.python/python-infthread.exp
new file mode 100644
index 0000000..c7ab063
--- /dev/null
+++ b/gdb/testsuite/gdb.python/python-infthread.exp
@@ -0,0 +1,88 @@
+# Copyright (C) 2009 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 "python-infthread"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    untested "Couldn't compile ${srcfile}"
+    return -1
+}
+
+# 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 } }
+  }
+}
+
+# Usage: gdb_py_test_multiple NAME INPUT RESULT {INPUT RESULT}...
+# Run a test named NAME, consisting of multiple lines of input.
+# After each input line INPUT, search for result line RESULT.
+# Succeed if all results are seen; fail otherwise.
+proc gdb_py_test_multiple {name args} {
+    global gdb_prompt
+    foreach {input result} $args {
+	if {[gdb_test_multiple $input "$name - $input" {
+	    -re "\[\r\n\]*($result)\[\r\n\]+($gdb_prompt | *>)$" {
+		pass "$name - $input"
+	    }
+	}]} {
+	    return 1
+	}
+    }
+    return 0
+}
+
+# Start with a fresh gdb.
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+gdb_test_multiple "python print 'hello, world!'" "verify python support" {
+    -re "not supported.*$gdb_prompt $"	{
+      unsupported "python support is disabled"
+      return -1
+    }
+    -re "$gdb_prompt $"	{}
+}
+
+# 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"



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