Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [rfc] python API exposing inferior's frame stack.
@ 2009-03-10 18:00 Thiago Jung Bauermann
  2009-03-10 23:37 ` Eli Zaretskii
  0 siblings, 1 reply; 19+ messages in thread
From: Thiago Jung Bauermann @ 2009-03-10 18:00 UTC (permalink / raw)
  To: gdb-patches ml

Hi,

This patch allows a Python script to work with the inferior's frame
stack. For now, the only "entry point" for this API is the
gdb.selected_frame function. The Python branch has also gdb.frames and
gdb.newest_frame, but they will need to be revised to acommodate
multiple inferiors, so I'm leaving them out for now. AFAIK even with
multiple inferiors, GDB has only one selected frame, so
gdb._selected_frame won't need to change.

This patch assumes the convenience function patch I re-posted a few days
ago. With these two patches, you can implement a convenience function
which checks the name of the function calling the selected function, as
follows:

import gdb
import re

class CallerIs (gdb.Function):
    """Return True if the calling function's name is equal to a string.
This function takes one or two arguments.
The first argument is the name of a function; if the calling function's
name is equal to this argument, this function returns True.
The optional second argument tells this function how many stack frames
to traverse to find the calling function.  The default is 1."""

    def __init__ (self):
        super (CallerIs, self).__init__ ("caller_is")

    def invoke (self, name, nframes = 1):
        frame = gdb.selected_frame ()
        while nframes > 0:
            frame = frame.older ()
            nframes = nframes - 1
        return frame.name () == name.string ()

class CallerMatches (gdb.Function):
    """Return True if the calling function's name matches a string.
This function takes one or two arguments.
The first argument is a regular expression; if the calling function's
name is matched by this argument, this function returns True.
The optional second argument tells this function how many stack frames
to traverse to find the calling function.  The default is 1."""

    def __init__ (self):
        super (CallerMatches, self).__init__ ("caller_matches")

    def invoke (self, name, nframes = 1):
        frame = gdb.selected_frame ()
        while nframes > 0:
            frame = frame.older ()
            nframes = nframes - 1
        return re.match (name.string (), frame.name ()) is not None

CallerIs()
CallerMatches()


These functions are useful for breakpoint conditions, if you want a
breakpoint to stop at a function only if it's invoked by certain
callers. In fact, using the gdb.Frame.read_var method, you can even stop
only if a function argument or a variable in scope has a certain value.

Regression tested on ppc-linux. WDYT?
-- 
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center


gdb/
2009-03-10  Thiago Jung Bauermann  <bauerman@br.ibm.com>

	* Makefile.in (SUBDIR_PYTHON_OBS): Add python-frame.o.
	(SUBDIR_PYTHON_SRCS): Add python-frame.c.
	(python-frame.o): New target.
	* python/python-frame.c: New file.
	* python/python-internal.h (gdbpy_frames, gdbpy_newest_frame,
	gdbpy_frame_stop_reason_string, gdbpy_selected_frame,
	gdbpy_initialize_frames): New prototypes.
	* python/python.c (_initialize_python): Call gdbpy_initialize_frames.
	(GdbMethods): Add `selected_frame' and `frame_stop_reason_string'
	entries.
	* stack.c (find_frame_funname): New function, factored out of
	print_frame.
	(print_frame): Call find_frame_funname.
	* stack.h (find_frame_funname): Add prototype.

gdb/doc/
2009-03-10  Thiago Jung Bauermann  <bauerman@br.ibm.com>

	* gdb.texinfo (Frames in Python): New node.
	(Python API): Update.

gdb/testsuite/
2009-03-10  Thiago Jung Bauermann  <bauerman@br.ibm.com>

	* gdb.python/python-frame.c: New file.
	* gdb.python/python-frame.exp: New file.
 
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 6334f65..d2a6348 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -271,12 +271,14 @@ SUBDIR_TUI_CFLAGS= \
 SUBDIR_PYTHON_OBS = \
 	python.o \
 	python-cmd.o \
+	python-frame.o \
 	python-function.o \
 	python-utils.o \
 	python-value.o
 SUBDIR_PYTHON_SRCS = \
 	python/python.c \
 	python/python-cmd.c \
+	python/python-frame.c \
 	python/python-function.c \
 	python/python-utils.c \
 	python/python-value.c
@@ -1851,6 +1853,10 @@ python-cmd.o: $(srcdir)/python/python-cmd.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-cmd.c
 	$(POSTCOMPILE)
 
+python-frame.o: $(srcdir)/python/python-frame.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-frame.c
+	$(POSTCOMPILE)
+
 python-function.o: $(srcdir)/python/python-function.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-function.c
 	$(POSTCOMPILE)
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 5533884..286a676 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -18155,6 +18155,7 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
 * Values From Inferior::
 * Commands In Python::          Implementing new commands in Python.
 * Functions In Python::         Writing new convenience functions.
+* Frames In Python::            Acessing inferior stack frames from Python.
 @end menu
 
 @node Basic Python
@@ -18650,6 +18651,86 @@ registration of the function with @value{GDBN}.  Depending on how the
 Python code is read into @value{GDBN}, you may need to import the
 @code{gdb} module explicitly.
 
+@node Frames In Python
+@subsubsection Acessing inferior stack frames from Python.
+
+@cindex frames in python
+@tindex gdb.Frame
+@tindex Frame
+When the debugged program stops, @value{GDBN} is able to analyse its call
+stack (@pxref{Frames,,Stack frames}).  The @code{gdb.Frame} class
+represents a frame in the stack.  A @code{gdb.Frame} object is only valid
+while its corresponding frame exists in the inferior's stack.  If you try
+to use an invalid frame object, a @code{RuntimeError} exception will be
+thrown.
+
+The following frame-related functions are available in the @code{gdb} module:
+
+@findex gdb.selected_frame
+@defun selected_frame
+Return the selected frame object.  (@pxref{Selection,,Selecting a Frame}).
+@end defun
+
+@findex gdb.frame_stop_reason_string
+@defun frame_stop_reason_string @var{reason}
+Return a string explaining the reason why @value{GDBN} stopped unwinding
+frames, as expressed by the given @var{reason} code (an integer, see the
+@code{unwind_stop_reason} method further down in this section).
+@end defun
+
+A @code{gdb.Frame} object has the following methods:
+
+@table @code
+@defmethod Frame equals @code{frame}
+Compare frames.
+@end defmethod
+
+@defmethod Frame is_valid
+Returns true if the @code{gdb.Frame} object is valid, false if not.
+A frame object can become invalid if the frame it refers to doesn't
+exist anymore in the inferior.  All @code{gdb.Frame} methods will throw
+an exception if it is invalid at the time the method call is made.
+@end defmethod
+
+@defmethod Frame name
+Returns the function name of the frame, or @code{None} if it can't be
+obtained.
+@end defmethod
+
+@defmethod Frame type
+Returns the type of the frame. The value can be one of
+@code{gdb.NORMAL_FRAME}, @code{gdb.DUMMY_FRAME}, @code{gdb.SIGTRAMP_FRAME}
+or @code{gdb.SENTINEL_FRAME}.
+@end defmethod
+
+@defmethod Frame unwind_stop_reason
+Return an integer representing the reason why it's not possible to find
+frames older than this.  Use @code{gdb.frame_stop_reason_string} to convert
+the value returned by this function to a string.
+@end defmethod
+
+@defmethod Frame pc
+Returns the frame's resume address.
+@end defmethod
+
+@defmethod Frame address_in_block
+Returns an address which falls within the frame's code block.
+@end defmethod
+
+@defmethod Frame older
+Return the frame immediately older (outer) to this frame.
+@end defmethod
+
+@defmethod Frame newer
+Return the frame immetidaely newer (inner) to this frame.
+@end defmethod
+
+@defmethod Frame read_var @var{variable}
+Return the value of the given variable in this frame.  @code{variable} must
+be a string.
+@end defmethod
+@end table
+
 @node Interpreters
 @chapter Command Interpreters
 @cindex command interpreters
diff --git a/gdb/python/python-frame.c b/gdb/python/python-frame.c
new file mode 100644
index 0000000..f7014c9
--- /dev/null
+++ b/gdb/python/python-frame.c
@@ -0,0 +1,556 @@
+/* Python interface to stack frames
+
+   Copyright (C) 2008, 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 "charset.h"
+#include "block.h"
+#include "frame.h"
+#include "exceptions.h"
+#include "symtab.h"
+#include "stack.h"
+#include "value.h"
+#include "python-internal.h"
+
+typedef struct {
+  PyObject_HEAD
+  struct frame_id frame_id;
+  struct gdbarch *gdbarch;
+
+  /* Marks that the FRAME_ID member actually holds the ID of the frame next
+     to this, and not this frames' ID itself.  This is a hack to permit Python
+     frame objects which represent invalid frames (i.e., the last frame_info
+     in a corrupt stack).  The problem arises from the fact that this code
+     relies on FRAME_ID to uniquely identify a frame, which is not always true
+     for the last "frame" in a corrupt stack (it can have a null ID, or the same
+     ID as the  previous frame).  Whenever get_prev_frame returns NULL, we
+     record the frame_id of the next frame and set FRAME_ID_IS_NEXT to 1.  */
+  int frame_id_is_next;
+} frame_object;
+
+/* Require a valid frame.  This must be called inside a TRY_CATCH, or
+   another context in which a gdb exception is allowed.  */
+#define FRAPY_REQUIRE_VALID(frame_obj, frame)		\
+    do {						\
+      frame = frame_object_to_frame_info (frame_obj);	\
+      if (frame == NULL)				\
+	error ("Frame is invalid.");			\
+    } while (0)
+
+static PyTypeObject frame_object_type;
+
+/* Returns the frame_info object corresponding to the given Python Frame
+   object.  If the frame doesn't exist anymore (the frame id doesn't
+   correspond to any frame in the inferior), returns NULL.  */
+
+static struct frame_info *
+frame_object_to_frame_info (frame_object *frame_obj)
+{
+  struct frame_info *frame;
+
+  frame = frame_find_by_id (frame_obj->frame_id);
+  if (frame == NULL)
+    return NULL;
+
+  if (frame_obj->frame_id_is_next)
+    frame = get_prev_frame (frame);
+
+  return frame;
+}
+
+/* Called by the Python interpreter to obtain string representation
+   of the object.  */
+
+static PyObject *
+frapy_str (PyObject *self)
+{
+  char *s;
+  long len;
+  PyObject *result;
+  struct ui_file *strfile;
+
+  strfile = mem_fileopen ();
+  fprint_frame_id (strfile, ((frame_object *) self)->frame_id);
+  s = ui_file_xstrdup (strfile, &len);
+  result = PyString_FromString (s);
+  xfree (s);
+
+  return result;
+}
+
+/* Implementation of gdb.Frame.is_valid (self) -> Boolean.
+   Returns True if the frame corresponding to the frame_id of this
+   object still exists in the inferior.  */
+
+static PyObject *
+frapy_is_valid (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame;
+
+  frame = frame_object_to_frame_info ((frame_object *) self);
+  if (frame == NULL)
+    Py_RETURN_FALSE;
+
+  Py_RETURN_TRUE;
+}
+
+/* Implementation of gdb.Frame.equals (self, other) -> Boolean. */
+
+static PyObject *
+frapy_equal_p (PyObject *self, PyObject *args)
+{
+  int equalp = 0;	  /* Initialize to appease gcc warning.  */
+  frame_object *self_frame = (frame_object *) self;
+  frame_object *other;
+  volatile struct gdb_exception except;
+
+  if (!PyArg_ParseTuple (args, "O!", &frame_object_type, &other))
+    return NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      equalp = frame_id_eq (self_frame->frame_id, other->frame_id);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  if (equalp)
+    Py_RETURN_TRUE;
+
+  Py_RETURN_FALSE;
+}
+
+/* Implementation of gdb.Frame.name (self) -> String.
+   Returns the name of the function corresponding to this frame.  */
+
+static PyObject *
+frapy_name (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame;
+  char *name;
+  enum language lang;
+  PyObject *result;
+  volatile struct gdb_exception except;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+      find_frame_funname (frame, &name, &lang);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  if (name)
+    result = target_string_to_unicode (name, strlen (name));
+  else
+    {
+      result = Py_None;
+      Py_INCREF (Py_None);
+    }
+
+  return result;
+}
+
+/* Implementation of gdb.Frame.type (self) -> Integer.
+   Returns the frame type, namely one of the gdb.*_FRAME constants.  */
+
+static PyObject *
+frapy_type (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame;
+  enum frame_type type = NORMAL_FRAME;/* Initialize to appease gcc warning.  */
+  volatile struct gdb_exception except;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+      type = get_frame_type (frame);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  return PyInt_FromLong (type);
+}
+
+/* Implementation of gdb.Frame.unwind_stop_reason (self) -> Integer.
+   Returns one of the gdb.FRAME_UNWIND_* constants.  */
+
+static PyObject *
+frapy_unwind_stop_reason (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame = NULL;    /* Initialize to appease gcc warning.  */
+  volatile struct gdb_exception except;
+  enum unwind_stop_reason stop_reason;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  stop_reason = get_frame_unwind_stop_reason (frame);
+
+  return PyInt_FromLong (stop_reason);
+}
+
+/* Implementation of gdb.Frame.pc (self) -> Long.
+   Returns the frame's resume address.  */
+
+static PyObject *
+frapy_pc (PyObject *self, PyObject *args)
+{
+  CORE_ADDR pc = 0;	      /* Initialize to appease gcc warning.  */
+  struct frame_info *frame;
+  volatile struct gdb_exception except;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+      pc = get_frame_pc (frame);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  return PyLong_FromUnsignedLongLong (pc);
+}
+
+/* Implementation of gdb.Frame.addr_in_block (self) -> Long.
+   Returns an address which falls within the frame's code block.  */
+
+static PyObject *
+frapy_addr_in_block (PyObject *self, PyObject *args)
+{
+  CORE_ADDR pc = 0;	      /* Initialize to appease gcc warning.  */
+  struct frame_info *frame;
+  volatile struct gdb_exception except;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+      pc = get_frame_address_in_block (frame);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  return PyLong_FromUnsignedLongLong (pc);
+}
+
+/* Convert a frame_info struct to a Python Frame object.
+   Sets a Python exception and returns NULL on error.  */
+
+static frame_object *
+frame_info_to_frame_object (struct frame_info *frame)
+{
+  frame_object *frame_obj;
+
+  frame_obj = PyObject_New (frame_object, &frame_object_type);
+  if (frame_obj == NULL)
+    {
+      PyErr_SetString (PyExc_MemoryError, "Could not allocate frame object.");
+      return NULL;
+    }
+
+  /* Try to get the previous frame, to determine if this is the last frame
+     in a corrupt stack.  If so, we need to store the frame_id of the next
+     frame and not of this one (which is possibly invalid).  */
+  if (get_prev_frame (frame) == NULL
+      && get_frame_unwind_stop_reason (frame) != UNWIND_NO_REASON
+      && get_next_frame (frame) != NULL)
+    {
+      frame_obj->frame_id = get_frame_id (get_next_frame (frame));
+      frame_obj->frame_id_is_next = 1;
+    }
+  else
+    {
+      frame_obj->frame_id = get_frame_id (frame);
+      frame_obj->frame_id_is_next = 0;
+    }
+
+  frame_obj->gdbarch = get_frame_arch (frame);
+
+  return frame_obj;
+}
+
+/* Implementation of gdb.Frame.older (self) -> gdb.Frame.
+   Returns the frame immediately older (outer) to this frame, or None if
+   there isn't one.  */
+
+static PyObject *
+frapy_older (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame, *prev;
+  volatile struct gdb_exception except;
+  PyObject *prev_obj = NULL;   /* Initialize to appease gcc warning.  */
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+      prev = get_prev_frame (frame);
+      if (prev)
+	prev_obj = (PyObject *) frame_info_to_frame_object (prev);
+      else
+	{
+	  Py_INCREF (Py_None);
+	  prev_obj = Py_None;
+	}
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  return prev_obj;
+}
+
+/* Implementation of gdb.Frame.newer (self) -> gdb.Frame.
+   Returns the frame immediately newer (inner) to this frame, or None if
+   there isn't one.  */
+
+static PyObject *
+frapy_newer (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame, *next;
+  volatile struct gdb_exception except;
+  PyObject *next_obj = NULL;   /* Initialize to appease gcc warning.  */
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+      next = get_next_frame (frame);
+      if (next)
+	next_obj = (PyObject *) frame_info_to_frame_object (next);
+      else
+	{
+	  Py_INCREF (Py_None);
+	  next_obj = Py_None;
+	}
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  return next_obj;
+}
+
+/* Implementation of gdb.Frame.read_var_value (self, variable) -> gdb.Value.
+   Returns the value of the given variable in this frame.  The argument must be
+   a string.  Returns None if GDB can't find the specified variable.  */
+
+static PyObject *
+frapy_read_var (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame;
+  PyObject *sym_obj;
+  struct symbol *var = NULL;	/* gcc-4.3.2 false warning.  */
+  struct value *val = NULL;
+  volatile struct gdb_exception except;
+
+  if (!PyArg_ParseTuple (args, "O", &sym_obj))
+    return NULL;
+
+  if (gdbpy_is_string (sym_obj))
+    {
+      char *var_name;
+      struct block *block = NULL;
+      struct cleanup *cleanup;
+      volatile struct gdb_exception except;
+
+      var_name = python_string_to_target_string (sym_obj);
+      if (!var_name)
+      	return NULL;
+      cleanup = make_cleanup (xfree, var_name);
+
+      TRY_CATCH (except, RETURN_MASK_ALL)
+	{
+	  FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+	  block = block_for_pc (get_frame_address_in_block (frame));
+	  var = lookup_symbol (var_name, block, VAR_DOMAIN, NULL);
+	}
+      GDB_PY_HANDLE_EXCEPTION (except);
+
+      if (!var)
+	{
+	  PyErr_Format (PyExc_ValueError,
+			_("variable '%s' not found"), var_name);
+	  do_cleanups (cleanup);
+
+	  return NULL;
+	}
+
+      do_cleanups (cleanup);
+    }
+  else
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       _("argument must be a symbol or string"));
+      return NULL;
+    }
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+      val = read_var_value (var, frame);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  if (val)
+    return value_to_value_object (val);
+
+  Py_RETURN_NONE;
+}
+
+/* Implementation of gdb.selected_frame () -> gdb.Frame.
+   Returns the selected frame object.  */
+
+PyObject *
+gdbpy_selected_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_selected_frame ("No frame is currently selected.");
+      frame_obj = frame_info_to_frame_object (frame);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  return (PyObject *) frame_obj;
+}
+
+/* Implementation of gdb.stop_reason_string (Integer) -> String.
+   Return a string explaining the unwind stop reason.  */
+
+PyObject *
+gdbpy_frame_stop_reason_string (PyObject *self, PyObject *args)
+{
+  int reason;
+  const char *str;
+
+  if (!PyArg_ParseTuple (args, "i", &reason))
+    return NULL;
+
+  if (reason < 0 || reason > UNWIND_NO_SAVED_PC)
+    {
+      PyErr_SetString (PyExc_ValueError, "Invalid frame stop reason.");
+      return NULL;
+    }
+
+  str = frame_stop_reason_string (reason);
+  return PyUnicode_Decode (str, strlen (str), host_charset (), NULL);
+}
+
+/* Sets up the Frame API in the gdb module.  */
+
+void
+gdbpy_initialize_frames (void)
+{
+  frame_object_type.tp_new = PyType_GenericNew;
+  if (PyType_Ready (&frame_object_type) < 0)
+    return;
+
+  /* Note: These would probably be best exposed as class attributes of Frame,
+     but I don't know how to do it except by messing with the type's dictionary.
+     That seems too messy.  */
+  PyModule_AddIntConstant (gdb_module, "NORMAL_FRAME", NORMAL_FRAME);
+  PyModule_AddIntConstant (gdb_module, "DUMMY_FRAME", DUMMY_FRAME);
+  PyModule_AddIntConstant (gdb_module, "SIGTRAMP_FRAME", SIGTRAMP_FRAME);
+  PyModule_AddIntConstant (gdb_module, "SENTINEL_FRAME", SENTINEL_FRAME);
+  PyModule_AddIntConstant (gdb_module,
+			   "FRAME_UNWIND_NO_REASON", UNWIND_NO_REASON);
+  PyModule_AddIntConstant (gdb_module,
+			   "FRAME_UNWIND_NULL_ID", UNWIND_NULL_ID);
+  PyModule_AddIntConstant (gdb_module,
+			   "FRAME_UNWIND_FIRST_ERROR", UNWIND_FIRST_ERROR);
+  PyModule_AddIntConstant (gdb_module,
+			   "FRAME_UNWIND_INNER_ID", UNWIND_INNER_ID);
+  PyModule_AddIntConstant (gdb_module,
+			   "FRAME_UNWIND_SAME_ID", UNWIND_SAME_ID);
+  PyModule_AddIntConstant (gdb_module,
+			   "FRAME_UNWIND_NO_SAVED_PC", UNWIND_NO_SAVED_PC);
+
+  Py_INCREF (&frame_object_type);
+  PyModule_AddObject (gdb_module, "Frame", (PyObject *) &frame_object_type);
+}
+
+\f
+
+static PyMethodDef frame_object_methods[] = {
+  { "equals", frapy_equal_p, METH_VARARGS,
+    "equals (frame) -> Boolean.\n\
+Compare this frame to the given frame." },
+  { "is_valid", frapy_is_valid, METH_NOARGS,
+    "is_valid () -> Boolean.\n\
+Return true if this frame is valid, false if not." },
+  { "name", frapy_name, METH_NOARGS,
+    "name () -> String.\n\
+Return the function name of the frame, or None if it can't be determined." },
+  { "type", frapy_type, METH_NOARGS,
+    "type () -> Integer.\n\
+Return the type of the frame." },
+  { "unwind_stop_reason", frapy_unwind_stop_reason, METH_NOARGS,
+    "unwind_stop_reason () -> Integer.\n\
+Return the reason why it's not possible to find frames older than this." },
+  { "pc", frapy_pc, METH_NOARGS,
+    "pc () -> Long.\n\
+Return the frame's resume address." },
+  { "addr_in_block", frapy_addr_in_block, METH_NOARGS,
+    "addr_in_block () -> Long.\n\
+Return an address which falls within the frame's code block." },
+  { "older", frapy_older, METH_NOARGS,
+    "older () -> gdb.Frame.\n\
+Return the frame immediately older (outer) to this frame." },
+  { "newer", frapy_newer, METH_NOARGS,
+    "newer () -> gdb.Frame.\n\
+Return the frame immetidaely newer (inner) to this frame." },
+  { "read_var", frapy_read_var, METH_VARARGS,
+    "read_var (variable) -> gdb.Value.\n\
+Return the value of the variable in this frame." },
+  {NULL}  /* Sentinel */
+};
+
+static PyTypeObject frame_object_type = {
+  PyObject_HEAD_INIT (NULL)
+  0,				  /* ob_size */
+  "gdb.Frame",			  /* tp_name */
+  sizeof (frame_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 */
+  frapy_str,			  /* tp_str */
+  0,				  /* tp_getattro */
+  0,				  /* tp_setattro */
+  0,				  /* tp_as_buffer */
+  Py_TPFLAGS_DEFAULT,		  /* tp_flags */
+  "GDB frame object",		  /* tp_doc */
+  0,				  /* tp_traverse */
+  0,				  /* tp_clear */
+  0,				  /* tp_richcompare */
+  0,				  /* tp_weaklistoffset */
+  0,				  /* tp_iter */
+  0,				  /* tp_iternext */
+  frame_object_methods		  /* tp_methods */
+};
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 463f08e..f8d0896 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -64,12 +64,15 @@ extern PyObject *gdb_module;
 extern PyTypeObject value_object_type;
 
 PyObject *gdbpy_history (PyObject *self, PyObject *args);
+PyObject *gdbpy_frame_stop_reason_string (PyObject *, PyObject *);
+PyObject *gdbpy_selected_frame (PyObject *self, PyObject *args);
 
 PyObject *value_to_value_object (struct value *v);
 
 struct value *convert_value_from_python (PyObject *obj);
 
 void gdbpy_initialize_values (void);
+void gdbpy_initialize_frames (void);
 void gdbpy_initialize_commands (void);
 void gdbpy_initialize_functions (void);
 
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 29f83bb..02d97e3 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -412,6 +412,7 @@ Enables or disables printing of Python stack traces."),
   PyModule_AddStringConstant (gdb_module, "TARGET_CONFIG", (char*) target_name);
 
   gdbpy_initialize_values ();
+  gdbpy_initialize_frames ();
   gdbpy_initialize_commands ();
   gdbpy_initialize_functions ();
 
@@ -465,6 +466,13 @@ static PyMethodDef GdbMethods[] =
   { "get_parameter", get_parameter, METH_VARARGS,
     "Return a gdb parameter's value" },
 
+  { "selected_frame", gdbpy_selected_frame, METH_NOARGS,
+    "selected_frame () -> gdb.Frame.\n\
+Return the selected frame object." },
+  { "frame_stop_reason_string", gdbpy_frame_stop_reason_string, METH_VARARGS,
+    "stop_reason_string (Integer) -> String.\n\
+Return a string explaining unwind stop reason." },
+
   { "write", gdbpy_write, METH_VARARGS,
     "Write a string using gdb's filtered stream." },
   { "flush", gdbpy_flush, METH_NOARGS,
diff --git a/gdb/stack.c b/gdb/stack.c
index d0c872e..5e4fb4d 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -579,20 +579,16 @@ print_frame_info (struct frame_info *frame, int print_level,
   gdb_flush (gdb_stdout);
 }
 
-static void
-print_frame (struct frame_info *frame, int print_level,
-	     enum print_what print_what, int print_args,
-	     struct symtab_and_line sal)
+/* Attempt to obtain the FUNNAME and FUNLANG of the function corresponding
+   to FRAME.  */
+void
+find_frame_funname (struct frame_info *frame, char **funname,
+		    enum language *funlang)
 {
   struct symbol *func;
-  char *funname = NULL;
-  enum language funlang = language_unknown;
-  struct ui_stream *stb;
-  struct cleanup *old_chain, *list_chain;
-  struct value_print_options opts;
 
-  stb = ui_out_stream_new (uiout);
-  old_chain = make_cleanup_ui_out_stream_delete (stb);
+  *funname = NULL;
+  *funlang = language_unknown;
 
   func = find_pc_function (get_frame_address_in_block (frame));
   if (func)
@@ -625,24 +621,24 @@ print_frame (struct frame_info *frame, int print_level,
 	  /* We also don't know anything about the function besides
 	     its address and name.  */
 	  func = 0;
-	  funname = SYMBOL_PRINT_NAME (msymbol);
-	  funlang = SYMBOL_LANGUAGE (msymbol);
+	  *funname = SYMBOL_PRINT_NAME (msymbol);
+	  *funlang = SYMBOL_LANGUAGE (msymbol);
 	}
       else
 	{
-	  funname = SYMBOL_PRINT_NAME (func);
-	  funlang = SYMBOL_LANGUAGE (func);
-	  if (funlang == language_cplus)
+	  *funname = SYMBOL_PRINT_NAME (func);
+	  *funlang = SYMBOL_LANGUAGE (func);
+	  if (*funlang == language_cplus)
 	    {
 	      /* It seems appropriate to use SYMBOL_PRINT_NAME() here,
 		 to display the demangled name that we already have
 		 stored in the symbol table, but we stored a version
 		 with DMGL_PARAMS turned on, and here we don't want to
 		 display parameters.  So remove the parameters.  */
-	      char *func_only = cp_remove_params (funname);
+	      char *func_only = cp_remove_params (*funname);
 	      if (func_only)
 		{
-		  funname = func_only;
+		  *funname = func_only;
 		  make_cleanup (xfree, func_only);
 		}
 	    }
@@ -655,10 +651,27 @@ print_frame (struct frame_info *frame, int print_level,
 
       if (msymbol != NULL)
 	{
-	  funname = SYMBOL_PRINT_NAME (msymbol);
-	  funlang = SYMBOL_LANGUAGE (msymbol);
+	  *funname = SYMBOL_PRINT_NAME (msymbol);
+	  *funlang = SYMBOL_LANGUAGE (msymbol);
 	}
     }
+}
+
+static void
+print_frame (struct frame_info *frame, int print_level,
+	     enum print_what print_what, int print_args,
+	     struct symtab_and_line sal)
+{
+  char *funname = NULL;
+  enum language funlang = language_unknown;
+  struct ui_stream *stb;
+  struct cleanup *old_chain, *list_chain;
+  struct value_print_options opts;
+
+  stb = ui_out_stream_new (uiout);
+  old_chain = make_cleanup_ui_out_stream_delete (stb);
+
+  find_frame_funname (frame, &funname, &funlang);
 
   annotate_frame_begin (print_level ? frame_relative_level (frame) : 0,
 			get_frame_pc (frame));
@@ -694,7 +707,7 @@ print_frame (struct frame_info *frame, int print_level,
       struct print_args_args args;
       struct cleanup *args_list_chain;
       args.frame = frame;
-      args.func = func;
+      args.func = find_pc_function (get_frame_address_in_block (frame));
       args.stream = gdb_stdout;
       args_list_chain = make_cleanup_ui_out_list_begin_end (uiout, "args");
       catch_errors (print_args_stub, &args, "", RETURN_MASK_ERROR);
diff --git a/gdb/stack.h b/gdb/stack.h
index 973a57f..56b1d91 100644
--- a/gdb/stack.h
+++ b/gdb/stack.h
@@ -22,4 +22,9 @@
 
 void select_frame_command (char *level_exp, int from_tty);
 
+/* Attempt to obtain the FUNNAME and FUNLANG of the function corresponding
+   to FRAME.  */
+void find_frame_funname (struct frame_info *frame, char **funname,
+			 enum language *funlang);
+
 #endif /* #ifndef STACK_H */
diff --git a/gdb/testsuite/gdb.python/python-frame.c b/gdb/testsuite/gdb.python/python-frame.c
new file mode 100644
index 0000000..22eb9f2
--- /dev/null
+++ b/gdb/testsuite/gdb.python/python-frame.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-frame.exp b/gdb/testsuite/gdb.python/python-frame.exp
new file mode 100644
index 0000000..d5fbd6d
--- /dev/null
+++ b/gdb/testsuite/gdb.python/python-frame.exp
@@ -0,0 +1,85 @@
+# 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 values to Python.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+set testfile "python-frame"
+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 f1 = gdb.selected_frame ()" "get second frame" 0
+gdb_py_test_silent_cmd "python f0 = f1.newer ()" "get first frame" 0
+
+gdb_test "python print 'result =', f0.equals (f1)" " = False" "test equals (false)"
+gdb_test "python print 'result =', f0.equals (f0)" " = True" "test equals (true)"
+gdb_test "python print 'result =', f0.is_valid ()" " = True" "test Frame.is_valid"
+gdb_test "python print 'result =', f0.name ()" " = f2" "test Frame.name"
+gdb_test "python print 'result =', f0.type () == gdb.NORMAL_FRAME" " = True" "test Frame.type"
+gdb_test "python print 'result =', f0.unwind_stop_reason () == gdb.FRAME_UNWIND_NO_REASON" " = True" "test Frame.type"
+gdb_test "python print 'result =', gdb.frame_stop_reason_string (gdb.FRAME_UNWIND_INNER_ID)" " = previous frame inner to this frame \\(corrupt stack\\?\\)" "test gdb.frame_stop_reason_string"
+gdb_test "python print 'result =', f0.pc ()" " = \[0-9\]+" "test Frame.pc"
+gdb_test "python print 'result =', f0.addr_in_block ()" " = \[0-9\]+" "test Frame.addr_in_block"
+gdb_test "python print 'result =', f0.older ().equals (f1)" " = True" "test Frame.older"
+gdb_test "python print 'result =', f1.newer ().equals (f0)" " = True" "test Frame.newer"
+gdb_test "python print 'result =', f0.read_var ('b')" \
+  "ValueError: variable 'b' not found.*Error while executing Python code." \
+  "test Frame.read_var - error"
+gdb_test "python print 'result =', f0.read_var ('a')" " = 1" "test Frame.read_var - success"



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

* Re: [rfc] python API exposing inferior's frame stack.
  2009-03-10 18:00 [rfc] python API exposing inferior's frame stack Thiago Jung Bauermann
@ 2009-03-10 23:37 ` Eli Zaretskii
  2009-03-15 18:35   ` Thiago Jung Bauermann
  2009-03-17 21:13   ` Tom Tromey
  0 siblings, 2 replies; 19+ messages in thread
From: Eli Zaretskii @ 2009-03-10 23:37 UTC (permalink / raw)
  To: Thiago Jung Bauermann; +Cc: gdb-patches

> From: Thiago Jung Bauermann <bauerman@br.ibm.com>
> Date: Tue, 10 Mar 2009 14:32:31 -0300
> 
> This patch allows a Python script to work with the inferior's frame
> stack. For now, the only "entry point" for this API is the
> gdb.selected_frame function.

Thanks.  I have a few comments about the documentation part of the
patch.

> +@tindex gdb.Frame
> +@tindex Frame

Please don't use @tindex, we don't use such an index in the GDB
manual.

> +When the debugged program stops, @value{GDBN} is able to analyse its call
                                                            ^^^^^^^
"analyze", please.  We use the US spelling.

>                                                               If you try
> +to use an invalid frame object, a @code{RuntimeError} exception will be
> +thrown.

Let's try to avoid unnecessary passive tense, as doing that makes the
text more concise and easy to read:

  If you try to use an invalid frame object, @value{GDBN} will throw a
  @code{RuntimeError} exception.

> +@findex gdb.selected_frame
> +@defun selected_frame

@defun automatically defines an entry in the index, so you don't need
to have another @findex for it.

> +@findex gdb.frame_stop_reason_string
> +@defun frame_stop_reason_string @var{reason}

Likewise.  Also, no need to use @var in the @defun line to decorate
arguments: @defun does that automatically for you.  (You do need to
use @var in the text that describes the function, such as below:

> +Return a string explaining the reason why @value{GDBN} stopped unwinding
> +frames, as expressed by the given @var{reason} code (an integer, see the
> +@code{unwind_stop_reason} method further down in this section).

> +@defmethod Frame equals @code{frame}

"frame" is an argument, right?  Then it again does not need any
markup, certainly not @code.

>                            All @code{gdb.Frame} methods will throw
> +an exception if it is invalid at the time the method call is made.
                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
"at the time the method is called".

> +Returns the type of the frame. The value can be one of
                                ^^
Two spaces between sentences, please.

> +@defmethod Frame unwind_stop_reason
> +Return an integer representing the reason why it's not possible to find
> +frames older than this.

"older"?  You mean, higher in the call stack?  Is "older" widespread
enough to be self-explanatory?

> +@defmethod Frame address_in_block
> +Returns an address which falls within the frame's code block.
> +@end defmethod

This is unclear to me.  Is there only one such address?  If not, why
is that useful to get _an_ address?

> +@defmethod Frame older
> +Return the frame immediately older (outer) to this frame.
> +@end defmethod
> +
> +@defmethod Frame newer
> +Return the frame immetidaely newer (inner) to this frame.
> +@end defmethod

Suggest to use "higher" or "above" or "towards the outermost frame".
Generally, try to use the terminology from  the "Examining the Stack"
chapter of the manual.

> +@defmethod Frame read_var @var{variable}

No need to use @var.

> +Return the value of the given variable in this frame.  @code{variable} must
                                                          ^^^^^^^^^^^^^^^
@var{variable}.


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

* Re: [rfc] python API exposing inferior's frame stack.
  2009-03-10 23:37 ` Eli Zaretskii
@ 2009-03-15 18:35   ` Thiago Jung Bauermann
  2009-03-15 19:30     ` Eli Zaretskii
  2009-03-17 21:13   ` Tom Tromey
  1 sibling, 1 reply; 19+ messages in thread
From: Thiago Jung Bauermann @ 2009-03-15 18:35 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches

El mar, 10-03-2009 a las 21:35 +0200, Eli Zaretskii escribió:
> > This patch allows a Python script to work with the inferior's frame
> > stack. For now, the only "entry point" for this API is the
> > gdb.selected_frame function.
> 
> Thanks.  I have a few comments about the documentation part of the
> patch.

Thanks for the review. The comments which I don't reply to below were
addressed.

> > +@defmethod Frame unwind_stop_reason
> > +Return an integer representing the reason why it's not possible to find
> > +frames older than this.
> 
> "older"?  You mean, higher in the call stack?

I don't know, which direction is "higher in the call stack"? :-)

> Is "older" widespread enough to be self-explanatory?

"older" is the name of the Frame method used to get the previous frame
(where "previous" is the convention used in the GDB source code), so it
has a good chance of being clear to the user of the Python API. In any
case, I reworded it to:

"Return an integer representing the reason why it's not possible to find
frames older (outer) than this."

What do you think?

> > +@defmethod Frame address_in_block
> > +Returns an address which falls within the frame's code block.
> > +@end defmethod
> 
> This is unclear to me.  Is there only one such address?  If not, why
> is that useful to get _an_ address?

The only use I have for this function right now is to obtain an address
to pass to the function gdb.find_pc_function, which returns a gdb.Symbol
object for the function containing the given address.

This usage directly reflects the way things are done in the GDB
internals. Perhaps I should depart from it, remove gdb.find_pc_function
and gdb.Frame.address_in_block and directly provide a gdb.Frame.function
method which returns the gdb.Symbol object for the function
corresponding to the frame?

It sounds like a better idea now that I think about it. But I'll have to
post a patch exposing inferior symbols before I can submit it upstream.

I'll remove gdb.Frame.address_in_block for now, then.

> > +@defmethod Frame older
> > +Return the frame immediately older (outer) to this frame.
> > +@end defmethod
> > +
> > +@defmethod Frame newer
> > +Return the frame immetidaely newer (inner) to this frame.
> > +@end defmethod
> 
> Suggest to use "higher" or "above" or "towards the outermost frame".
> Generally, try to use the terminology from  the "Examining the Stack"
> chapter of the manual.

IMHO, "inner" and "outer" are already conforming to the terminology from
the "Examining the Stack" chapter. It is a bit awkward to use "towards
the outermost frame" to describe these methods, e.g.:

"Return the next frame in the direction towards the outermost frame."

and

"Return the next frame in the direction towards the innermost frame."

Mmm... Now that I tried, doesn't sound too bad. But still I find my
original wording more direct and simpler to understand. What do you
think?

I'll post an updated patch when we agree on these points.
-- 
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center


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

* Re: [rfc] python API exposing inferior's frame stack.
  2009-03-15 18:35   ` Thiago Jung Bauermann
@ 2009-03-15 19:30     ` Eli Zaretskii
  2009-03-16  3:36       ` Thiago Jung Bauermann
  0 siblings, 1 reply; 19+ messages in thread
From: Eli Zaretskii @ 2009-03-15 19:30 UTC (permalink / raw)
  To: Thiago Jung Bauermann; +Cc: gdb-patches

> From: Thiago Jung Bauermann <bauerman@br.ibm.com>
> Cc: gdb-patches@sourceware.org
> Date: Sun, 15 Mar 2009 13:11:06 -0300
> 
> El mar, 10-03-2009 a las 21:35 +0200, Eli Zaretskii escribió:
> > > This patch allows a Python script to work with the inferior's frame
> > > stack. For now, the only "entry point" for this API is the
> > > gdb.selected_frame function.
> > 
> > Thanks.  I have a few comments about the documentation part of the
> > patch.
> 
> Thanks for the review. The comments which I don't reply to below were
> addressed.
> 
> > > +@defmethod Frame unwind_stop_reason
> > > +Return an integer representing the reason why it's not possible to find
> > > +frames older than this.
> > 
> > "older"?  You mean, higher in the call stack?
> 
> I don't know, which direction is "higher in the call stack"? :-)
> 
> > Is "older" widespread enough to be self-explanatory?
> 
> "older" is the name of the Frame method used to get the previous frame
> (where "previous" is the convention used in the GDB source code), so it
> has a good chance of being clear to the user of the Python API. In any
> case, I reworded it to:
> 
> "Return an integer representing the reason why it's not possible to find
> frames older (outer) than this."

I prefer "frames previous to this one".  We already use similar
wording in frame_stop_reason_string.

> This usage directly reflects the way things are done in the GDB
> internals. Perhaps I should depart from it, remove gdb.find_pc_function
> and gdb.Frame.address_in_block and directly provide a gdb.Frame.function
> method which returns the gdb.Symbol object for the function
> corresponding to the frame?
> 
> It sounds like a better idea now that I think about it. But I'll have to
> post a patch exposing inferior symbols before I can submit it upstream.
> 
> I'll remove gdb.Frame.address_in_block for now, then.

Sounds good, thanks.

> > > +@defmethod Frame older
> > > +Return the frame immediately older (outer) to this frame.
> > > +@end defmethod
> > > +
> > > +@defmethod Frame newer
> > > +Return the frame immetidaely newer (inner) to this frame.
> > > +@end defmethod
> > 
> > Suggest to use "higher" or "above" or "towards the outermost frame".
> > Generally, try to use the terminology from  the "Examining the Stack"
> > chapter of the manual.
> 
> IMHO, "inner" and "outer" are already conforming to the terminology from
> the "Examining the Stack" chapter. It is a bit awkward to use "towards
> the outermost frame" to describe these methods, e.g.:
> 
> "Return the next frame in the direction towards the outermost frame."
> 
> and
> 
> "Return the next frame in the direction towards the innermost frame."
> 
> Mmm... Now that I tried, doesn't sound too bad. But still I find my
> original wording more direct and simpler to understand. What do you
> think?

We use "innermost" in the manual much more than "inner".  You can also
use "previous" and "next" if you like that better.

Also note that there's a typo in the quoted fragment ("immetidaely").

Thanks.


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

* Re: [rfc] python API exposing inferior's frame stack.
  2009-03-15 19:30     ` Eli Zaretskii
@ 2009-03-16  3:36       ` Thiago Jung Bauermann
  2009-03-16  4:07         ` Eli Zaretskii
  0 siblings, 1 reply; 19+ messages in thread
From: Thiago Jung Bauermann @ 2009-03-16  3:36 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches

El dom, 15-03-2009 a las 21:21 +0200, Eli Zaretskii escribió:
> > From: Thiago Jung Bauermann <bauerman@br.ibm.com>
> > El mar, 10-03-2009 a las 21:35 +0200, Eli Zaretskii escribió:
> > > Is "older" widespread enough to be self-explanatory?
> > 
> > "older" is the name of the Frame method used to get the previous frame
> > (where "previous" is the convention used in the GDB source code), so it
> > has a good chance of being clear to the user of the Python API. In any
> > case, I reworded it to:
> > 
> > "Return an integer representing the reason why it's not possible to find
> > frames older (outer) than this."
> 
> I prefer "frames previous to this one".  We already use similar
> wording in frame_stop_reason_string.

I don't like "previous" and "next". They are not clear enough. I think
you prefer this only because you're used to its meaning in the GDB
source code. If they were clear enough, the following clarification
wouldn't be necessary in frame.h:

/* Given a FRAME, return the next (more inner, younger) or previous
   (more outer, older) frame.  */
extern struct frame_info *get_prev_frame (struct frame_info *);
extern struct frame_info *get_next_frame (struct frame_info *);

I explicitly avoided such wording in the Python Frame API, preferring to
use "older" and "newer". In the doc strings and documentation, I also
use "outer" and "inner", which I still think conforms to be stack
chapter in the GDB manual.

> > > > +@defmethod Frame older
> > > > +Return the frame immediately older (outer) to this frame.
> > > > +@end defmethod
> > > > +
> > > > +@defmethod Frame newer
> > > > +Return the frame immetidaely newer (inner) to this frame.
> > > > +@end defmethod
> > > 
> > > Suggest to use "higher" or "above" or "towards the outermost frame".
> > > Generally, try to use the terminology from  the "Examining the Stack"
> > > chapter of the manual.
> > 
> > IMHO, "inner" and "outer" are already conforming to the terminology from
> > the "Examining the Stack" chapter. It is a bit awkward to use "towards
> > the outermost frame" to describe these methods, e.g.:
> > 
> > "Return the next frame in the direction towards the outermost frame."
> > 
> > and
> > 
> > "Return the next frame in the direction towards the innermost frame."
> > 
> > Mmm... Now that I tried, doesn't sound too bad. But still I find my
> > original wording more direct and simpler to understand. What do you
> > think?
> 
> We use "innermost" in the manual much more than "inner".  You can also
> use "previous" and "next" if you like that better.

Why "innermost" is acceptable but "inner" is not? I don't see why the
different treatment. If one can be used, the other should be allowed
too, no?

> Also note that there's a typo in the quoted fragment ("immetidaely").

Fixed, thanks.
-- 
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center


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

* Re: [rfc] python API exposing inferior's frame stack.
  2009-03-16  3:36       ` Thiago Jung Bauermann
@ 2009-03-16  4:07         ` Eli Zaretskii
  2009-03-20 23:04           ` Thiago Jung Bauermann
  0 siblings, 1 reply; 19+ messages in thread
From: Eli Zaretskii @ 2009-03-16  4:07 UTC (permalink / raw)
  To: Thiago Jung Bauermann; +Cc: gdb-patches

> From: Thiago Jung Bauermann <bauerman@br.ibm.com>
> Cc: gdb-patches@sourceware.org
> Date: Sun, 15 Mar 2009 20:46:46 -0300
> 
> > I prefer "frames previous to this one".  We already use similar
> > wording in frame_stop_reason_string.
> 
> I don't like "previous" and "next". They are not clear enough. I think
> you prefer this only because you're used to its meaning in the GDB
> source code.

Not only in the code, in the messages we display to the user, which is
quite another thing.

> In the doc strings and documentation, I also use "outer" and
> "inner", which I still think conforms to be stack chapter in the GDB
> manual.

As I explained, "outer" or "inner" are almost unused in the manual.
We use "innermost" and "outermost".
> > We use "innermost" in the manual much more than "inner".  You can also
> > use "previous" and "next" if you like that better.
> 
> Why "innermost" is acceptable but "inner" is not?

Because the former is used a lot in the manual and explained in
several places.


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

* Re: [rfc] python API exposing inferior's frame stack.
  2009-03-10 23:37 ` Eli Zaretskii
  2009-03-15 18:35   ` Thiago Jung Bauermann
@ 2009-03-17 21:13   ` Tom Tromey
  2009-03-17 22:43     ` Eli Zaretskii
  1 sibling, 1 reply; 19+ messages in thread
From: Tom Tromey @ 2009-03-17 21:13 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Thiago Jung Bauermann, gdb-patches

>>>>> "Eli" == Eli Zaretskii <eliz@gnu.org> writes:

>> +@tindex gdb.Frame
>> +@tindex Frame

Eli> Please don't use @tindex, we don't use such an index in the GDB
Eli> manual.

Now that we have documentation describing types, it seems like we
ought to add a type index.

Tom


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

* Re: [rfc] python API exposing inferior's frame stack.
  2009-03-17 21:13   ` Tom Tromey
@ 2009-03-17 22:43     ` Eli Zaretskii
  2009-03-18  0:35       ` Tom Tromey
  0 siblings, 1 reply; 19+ messages in thread
From: Eli Zaretskii @ 2009-03-17 22:43 UTC (permalink / raw)
  To: tromey; +Cc: bauerman, gdb-patches

> Cc: Thiago Jung Bauermann <bauerman@br.ibm.com>, gdb-patches@sourceware.org
> From: Tom Tromey <tromey@redhat.com>
> Date: Tue, 17 Mar 2009 15:01:23 -0600
> 
> >>>>> "Eli" == Eli Zaretskii <eliz@gnu.org> writes:
> 
> >> +@tindex gdb.Frame
> >> +@tindex Frame
> 
> Eli> Please don't use @tindex, we don't use such an index in the GDB
> Eli> manual.
> 
> Now that we have documentation describing types, it seems like we
> ought to add a type index.

What's wrong with having them documented in the main index?  We can
always have something like

  @cindex @code{Foo}, a data type

More than one index in a manual complicates looking up things, and I
don't feel the number of types we have in GDB, or will have in the
near future, justifies this inconveniency.  But I'm open to arguments
to the contrary.


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

* Re: [rfc] python API exposing inferior's frame stack.
  2009-03-17 22:43     ` Eli Zaretskii
@ 2009-03-18  0:35       ` Tom Tromey
  2009-03-18  4:12         ` Eli Zaretskii
  0 siblings, 1 reply; 19+ messages in thread
From: Tom Tromey @ 2009-03-18  0:35 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: bauerman, gdb-patches

>>>>> "Eli" == Eli Zaretskii <eliz@gnu.org> writes:

Tom> Now that we have documentation describing types, it seems like we
Tom> ought to add a type index.

Eli> What's wrong with having them documented in the main index?

Nothing, that would also be fine by me.

In that case I still think it makes sense to use @tindex, and then use
@syncodeindex to merge the indices.  This way we maintain some
separation between the markup and the presentation.

Tom


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

* Re: [rfc] python API exposing inferior's frame stack.
  2009-03-18  0:35       ` Tom Tromey
@ 2009-03-18  4:12         ` Eli Zaretskii
  0 siblings, 0 replies; 19+ messages in thread
From: Eli Zaretskii @ 2009-03-18  4:12 UTC (permalink / raw)
  To: Tom Tromey; +Cc: bauerman, gdb-patches

> Cc: bauerman@br.ibm.com, gdb-patches@sourceware.org
> From: Tom Tromey <tromey@redhat.com>
> Date: Tue, 17 Mar 2009 18:07:58 -0600
> 
> In that case I still think it makes sense to use @tindex, and then use
> @syncodeindex to merge the indices.

Fine with me.


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

* Re: [rfc] python API exposing inferior's frame stack.
  2009-03-16  4:07         ` Eli Zaretskii
@ 2009-03-20 23:04           ` Thiago Jung Bauermann
  2009-03-21  8:34             ` Eli Zaretskii
  2009-03-22 16:00             ` Tom Tromey
  0 siblings, 2 replies; 19+ messages in thread
From: Thiago Jung Bauermann @ 2009-03-20 23:04 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches

El lun, 16-03-2009 a las 06:00 +0200, Eli Zaretskii escribió:
> > From: Thiago Jung Bauermann <bauerman@br.ibm.com>
> > Cc: gdb-patches@sourceware.org
> > Date: Sun, 15 Mar 2009 20:46:46 -0300
> > 
> > I don't like "previous" and "next". They are not clear enough. I think
> > you prefer this only because you're used to its meaning in the GDB
> > source code.
> 
> Not only in the code, in the messages we display to the user, which is
> quite another thing.
> 
> > Why "innermost" is acceptable but "inner" is not?
> 
> Because the former is used a lot in the manual and explained in
> several places.

Ok, I think I found a way to side-step our disagreement. The help
strings for the up and down commands say:

(gdb) help up
Select and print stack frame that called this one.
An argument says how many frames up to go.
(gdb) help down
Select and print stack frame called by this one.
An argument says how many frames down to go.
(gdb)

So I am changing the documentation and doc strings for Frame.older and
Frame.newer to use similar wording:

+@defmethod Frame older
+Return the frame that called this frame.
+@end defmethod
+
+@defmethod Frame newer
+Return the frame called by this frame.
+@end defmethod

I also changed Frame.unwind_stop_reason to use "outermost":

+@defmethod Frame unwind_stop_reason
+Return an integer representing the reason why it's not possible to find
+more frames toward the outermost frame.  Use
+@code{gdb.frame_stop_reason_string} to convert the value returned by this
+function to a string.
+@end defmethod

What do you think?

This patch also has a small code change to make frame_object_type's
tp_new member to be initialized in the variable's declaration rather
than in gdbpy_initialize_frames.
-- 
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center


diff --git a/gdb/ChangeLog.patches b/gdb/ChangeLog.patches
index 5e6fbbe..2ccfd48 100644
--- a/gdb/ChangeLog.patches
+++ b/gdb/ChangeLog.patches
@@ -1,4 +1,34 @@
 gdb/
2009-03-20  Thiago Jung Bauermann  <bauerman@br.ibm.com>

	* Makefile.in (SUBDIR_PYTHON_OBS): Add python-frame.o.
	(SUBDIR_PYTHON_SRCS): Add python-frame.c.
	(python-frame.o): New target.
	* python/python-frame.c: New file.
	* python/python-internal.h (gdbpy_frames, gdbpy_newest_frame,
	gdbpy_frame_stop_reason_string, gdbpy_selected_frame,
	gdbpy_initialize_frames): New prototypes.
	* python/python.c (_initialize_python): Call gdbpy_initialize_frames.
	(GdbMethods): Add `selected_frame' and `frame_stop_reason_string'
	entries.
	* stack.c (find_frame_funname): New function, factored out of
	print_frame.
	(print_frame): Call find_frame_funname.
	* stack.h (find_frame_funname): Add prototype.

gdb/doc/
2009-03-20  Thiago Jung Bauermann  <bauerman@br.ibm.com>

	* gdb.texinfo (Frames in Python): New node.
	(Python API): Update.

gdb/testsuite/
2009-03-20  Thiago Jung Bauermann  <bauerman@br.ibm.com>

	* gdb.python/python-frame.c: New file.
	* gdb.python/python-frame.exp: New file.

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 6334f65..d2a6348 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -271,12 +271,14 @@ SUBDIR_TUI_CFLAGS= \
 SUBDIR_PYTHON_OBS = \
 	python.o \
 	python-cmd.o \
+	python-frame.o \
 	python-function.o \
 	python-utils.o \
 	python-value.o
 SUBDIR_PYTHON_SRCS = \
 	python/python.c \
 	python/python-cmd.c \
+	python/python-frame.c \
 	python/python-function.c \
 	python/python-utils.c \
 	python/python-value.c
@@ -1851,6 +1853,10 @@ python-cmd.o: $(srcdir)/python/python-cmd.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-cmd.c
 	$(POSTCOMPILE)
 
+python-frame.o: $(srcdir)/python/python-frame.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-frame.c
+	$(POSTCOMPILE)
+
 python-function.o: $(srcdir)/python/python-function.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-function.c
 	$(POSTCOMPILE)
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 1034481..04d911c 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -18155,6 +18155,7 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
 * Values From Inferior::
 * Commands In Python::          Implementing new commands in Python.
 * Functions In Python::         Writing new convenience functions.
+* Frames In Python::            Acessing inferior stack frames from Python.
 @end menu
 
 @node Basic Python
@@ -18646,6 +18647,80 @@ registration of the function with @value{GDBN}.  Depending on how the
 Python code is read into @value{GDBN}, you may need to import the
 @code{gdb} module explicitly.
 
+@node Frames In Python
+@subsubsection Acessing inferior stack frames from Python.
+
+@cindex frames in python
+When the debugged program stops, @value{GDBN} is able to analyze its call
+stack (@pxref{Frames,,Stack frames}).  The @code{gdb.Frame} class
+represents a frame in the stack.  A @code{gdb.Frame} object is only valid
+while its corresponding frame exists in the inferior's stack.  If you try
+to use an invalid frame object, @value{GDBN} will throw a @code{RuntimeError}
+exception.
+
+The following frame-related functions are available in the @code{gdb} module:
+
+@findex gdb.selected_frame
+@defun selected_frame
+Return the selected frame object.  (@pxref{Selection,,Selecting a Frame}).
+@end defun
+
+@defun frame_stop_reason_string reason
+Return a string explaining the reason why @value{GDBN} stopped unwinding
+frames, as expressed by the given @var{reason} code (an integer, see the
+@code{unwind_stop_reason} method further down in this section).
+@end defun
+
+A @code{gdb.Frame} object has the following methods:
+
+@table @code
+@defmethod Frame equals frame
+Compare frames.
+@end defmethod
+
+@defmethod Frame is_valid
+Returns true if the @code{gdb.Frame} object is valid, false if not.
+A frame object can become invalid if the frame it refers to doesn't
+exist anymore in the inferior.  All @code{gdb.Frame} methods will throw
+an exception if it is invalid at the time the method is called.
+@end defmethod
+
+@defmethod Frame name
+Returns the function name of the frame, or @code{None} if it can't be
+obtained.
+@end defmethod
+
+@defmethod Frame type
+Returns the type of the frame.  The value can be one of
+@code{gdb.NORMAL_FRAME}, @code{gdb.DUMMY_FRAME}, @code{gdb.SIGTRAMP_FRAME}
+or @code{gdb.SENTINEL_FRAME}.
+@end defmethod
+
+@defmethod Frame unwind_stop_reason
+Return an integer representing the reason why it's not possible to find
+more frames toward the outermost frame.  Use
+@code{gdb.frame_stop_reason_string} to convert the value returned by this
+function to a string.
+@end defmethod
+
+@defmethod Frame pc
+Returns the frame's resume address.
+@end defmethod
+
+@defmethod Frame older
+Return the frame that called this frame.
+@end defmethod
+
+@defmethod Frame newer
+Return the frame called by this frame.
+@end defmethod
+
+@defmethod Frame read_var variable
+Return the value of the given variable in this frame.  @var{variable} must
+be a string.
+@end defmethod
+@end table
+
 @node Interpreters
 @chapter Command Interpreters
 @cindex command interpreters
diff --git a/gdb/python/python-frame.c b/gdb/python/python-frame.c
new file mode 100644
index 0000000..97e52ce
--- /dev/null
+++ b/gdb/python/python-frame.c
@@ -0,0 +1,541 @@
+/* Python interface to stack frames
+
+   Copyright (C) 2008, 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 "charset.h"
+#include "block.h"
+#include "frame.h"
+#include "exceptions.h"
+#include "symtab.h"
+#include "stack.h"
+#include "value.h"
+#include "python-internal.h"
+
+typedef struct {
+  PyObject_HEAD
+  struct frame_id frame_id;
+  struct gdbarch *gdbarch;
+
+  /* Marks that the FRAME_ID member actually holds the ID of the frame next
+     to this, and not this frames' ID itself.  This is a hack to permit Python
+     frame objects which represent invalid frames (i.e., the last frame_info
+     in a corrupt stack).  The problem arises from the fact that this code
+     relies on FRAME_ID to uniquely identify a frame, which is not always true
+     for the last "frame" in a corrupt stack (it can have a null ID, or the same
+     ID as the  previous frame).  Whenever get_prev_frame returns NULL, we
+     record the frame_id of the next frame and set FRAME_ID_IS_NEXT to 1.  */
+  int frame_id_is_next;
+} frame_object;
+
+/* Require a valid frame.  This must be called inside a TRY_CATCH, or
+   another context in which a gdb exception is allowed.  */
+#define FRAPY_REQUIRE_VALID(frame_obj, frame)		\
+    do {						\
+      frame = frame_object_to_frame_info (frame_obj);	\
+      if (frame == NULL)				\
+	error ("Frame is invalid.");			\
+    } while (0)
+
+static PyTypeObject frame_object_type;
+
+/* Returns the frame_info object corresponding to the given Python Frame
+   object.  If the frame doesn't exist anymore (the frame id doesn't
+   correspond to any frame in the inferior), returns NULL.  */
+
+static struct frame_info *
+frame_object_to_frame_info (frame_object *frame_obj)
+{
+  struct frame_info *frame;
+
+  frame = frame_find_by_id (frame_obj->frame_id);
+  if (frame == NULL)
+    return NULL;
+
+  if (frame_obj->frame_id_is_next)
+    frame = get_prev_frame (frame);
+
+  return frame;
+}
+
+/* Called by the Python interpreter to obtain string representation
+   of the object.  */
+
+static PyObject *
+frapy_str (PyObject *self)
+{
+  char *s;
+  long len;
+  PyObject *result;
+  struct ui_file *strfile;
+
+  strfile = mem_fileopen ();
+  fprint_frame_id (strfile, ((frame_object *) self)->frame_id);
+  s = ui_file_xstrdup (strfile, &len);
+  result = PyString_FromString (s);
+  xfree (s);
+
+  return result;
+}
+
+/* Implementation of gdb.Frame.is_valid (self) -> Boolean.
+   Returns True if the frame corresponding to the frame_id of this
+   object still exists in the inferior.  */
+
+static PyObject *
+frapy_is_valid (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame;
+
+  frame = frame_object_to_frame_info ((frame_object *) self);
+  if (frame == NULL)
+    Py_RETURN_FALSE;
+
+  Py_RETURN_TRUE;
+}
+
+/* Implementation of gdb.Frame.equals (self, other) -> Boolean. */
+
+static PyObject *
+frapy_equal_p (PyObject *self, PyObject *args)
+{
+  int equalp = 0;	  /* Initialize to appease gcc warning.  */
+  frame_object *self_frame = (frame_object *) self;
+  frame_object *other;
+  volatile struct gdb_exception except;
+
+  if (!PyArg_ParseTuple (args, "O!", &frame_object_type, &other))
+    return NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      equalp = frame_id_eq (self_frame->frame_id, other->frame_id);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  if (equalp)
+    Py_RETURN_TRUE;
+
+  Py_RETURN_FALSE;
+}
+
+/* Implementation of gdb.Frame.name (self) -> String.
+   Returns the name of the function corresponding to this frame.  */
+
+static PyObject *
+frapy_name (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame;
+  char *name;
+  enum language lang;
+  PyObject *result;
+  volatile struct gdb_exception except;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+      find_frame_funname (frame, &name, &lang);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  if (name)
+    result = target_string_to_unicode (name, strlen (name));
+  else
+    {
+      result = Py_None;
+      Py_INCREF (Py_None);
+    }
+
+  return result;
+}
+
+/* Implementation of gdb.Frame.type (self) -> Integer.
+   Returns the frame type, namely one of the gdb.*_FRAME constants.  */
+
+static PyObject *
+frapy_type (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame;
+  enum frame_type type = NORMAL_FRAME;/* Initialize to appease gcc warning.  */
+  volatile struct gdb_exception except;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+      type = get_frame_type (frame);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  return PyInt_FromLong (type);
+}
+
+/* Implementation of gdb.Frame.unwind_stop_reason (self) -> Integer.
+   Returns one of the gdb.FRAME_UNWIND_* constants.  */
+
+static PyObject *
+frapy_unwind_stop_reason (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame = NULL;    /* Initialize to appease gcc warning.  */
+  volatile struct gdb_exception except;
+  enum unwind_stop_reason stop_reason;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  stop_reason = get_frame_unwind_stop_reason (frame);
+
+  return PyInt_FromLong (stop_reason);
+}
+
+/* Implementation of gdb.Frame.pc (self) -> Long.
+   Returns the frame's resume address.  */
+
+static PyObject *
+frapy_pc (PyObject *self, PyObject *args)
+{
+  CORE_ADDR pc = 0;	      /* Initialize to appease gcc warning.  */
+  struct frame_info *frame;
+  volatile struct gdb_exception except;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+      pc = get_frame_pc (frame);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  return PyLong_FromUnsignedLongLong (pc);
+}
+
+/* Convert a frame_info struct to a Python Frame object.
+   Sets a Python exception and returns NULL on error.  */
+
+static frame_object *
+frame_info_to_frame_object (struct frame_info *frame)
+{
+  frame_object *frame_obj;
+
+  frame_obj = PyObject_New (frame_object, &frame_object_type);
+  if (frame_obj == NULL)
+    {
+      PyErr_SetString (PyExc_MemoryError, "Could not allocate frame object.");
+      return NULL;
+    }
+
+  /* Try to get the previous frame, to determine if this is the last frame
+     in a corrupt stack.  If so, we need to store the frame_id of the next
+     frame and not of this one (which is possibly invalid).  */
+  if (get_prev_frame (frame) == NULL
+      && get_frame_unwind_stop_reason (frame) != UNWIND_NO_REASON
+      && get_next_frame (frame) != NULL)
+    {
+      frame_obj->frame_id = get_frame_id (get_next_frame (frame));
+      frame_obj->frame_id_is_next = 1;
+    }
+  else
+    {
+      frame_obj->frame_id = get_frame_id (frame);
+      frame_obj->frame_id_is_next = 0;
+    }
+
+  frame_obj->gdbarch = get_frame_arch (frame);
+
+  return frame_obj;
+}
+
+/* Implementation of gdb.Frame.older (self) -> gdb.Frame.
+   Returns the frame immediately older (outer) to this frame, or None if
+   there isn't one.  */
+
+static PyObject *
+frapy_older (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame, *prev;
+  volatile struct gdb_exception except;
+  PyObject *prev_obj = NULL;   /* Initialize to appease gcc warning.  */
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+      prev = get_prev_frame (frame);
+      if (prev)
+	prev_obj = (PyObject *) frame_info_to_frame_object (prev);
+      else
+	{
+	  Py_INCREF (Py_None);
+	  prev_obj = Py_None;
+	}
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  return prev_obj;
+}
+
+/* Implementation of gdb.Frame.newer (self) -> gdb.Frame.
+   Returns the frame immediately newer (inner) to this frame, or None if
+   there isn't one.  */
+
+static PyObject *
+frapy_newer (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame, *next;
+  volatile struct gdb_exception except;
+  PyObject *next_obj = NULL;   /* Initialize to appease gcc warning.  */
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+      next = get_next_frame (frame);
+      if (next)
+	next_obj = (PyObject *) frame_info_to_frame_object (next);
+      else
+	{
+	  Py_INCREF (Py_None);
+	  next_obj = Py_None;
+	}
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  return next_obj;
+}
+
+/* Implementation of gdb.Frame.read_var_value (self, variable) -> gdb.Value.
+   Returns the value of the given variable in this frame.  The argument must be
+   a string.  Returns None if GDB can't find the specified variable.  */
+
+static PyObject *
+frapy_read_var (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame;
+  PyObject *sym_obj;
+  struct symbol *var = NULL;	/* gcc-4.3.2 false warning.  */
+  struct value *val = NULL;
+  volatile struct gdb_exception except;
+
+  if (!PyArg_ParseTuple (args, "O", &sym_obj))
+    return NULL;
+
+  if (gdbpy_is_string (sym_obj))
+    {
+      char *var_name;
+      struct block *block = NULL;
+      struct cleanup *cleanup;
+      volatile struct gdb_exception except;
+
+      var_name = python_string_to_target_string (sym_obj);
+      if (!var_name)
+      	return NULL;
+      cleanup = make_cleanup (xfree, var_name);
+
+      TRY_CATCH (except, RETURN_MASK_ALL)
+	{
+	  FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+	  block = block_for_pc (get_frame_address_in_block (frame));
+	  var = lookup_symbol (var_name, block, VAR_DOMAIN, NULL);
+	}
+      GDB_PY_HANDLE_EXCEPTION (except);
+
+      if (!var)
+	{
+	  PyErr_Format (PyExc_ValueError,
+			_("variable '%s' not found"), var_name);
+	  do_cleanups (cleanup);
+
+	  return NULL;
+	}
+
+      do_cleanups (cleanup);
+    }
+  else
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       _("argument must be a symbol or string"));
+      return NULL;
+    }
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+      val = read_var_value (var, frame);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  if (val)
+    return value_to_value_object (val);
+
+  Py_RETURN_NONE;
+}
+
+/* Implementation of gdb.selected_frame () -> gdb.Frame.
+   Returns the selected frame object.  */
+
+PyObject *
+gdbpy_selected_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_selected_frame ("No frame is currently selected.");
+      frame_obj = frame_info_to_frame_object (frame);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  return (PyObject *) frame_obj;
+}
+
+/* Implementation of gdb.stop_reason_string (Integer) -> String.
+   Return a string explaining the unwind stop reason.  */
+
+PyObject *
+gdbpy_frame_stop_reason_string (PyObject *self, PyObject *args)
+{
+  int reason;
+  const char *str;
+
+  if (!PyArg_ParseTuple (args, "i", &reason))
+    return NULL;
+
+  if (reason < 0 || reason > UNWIND_NO_SAVED_PC)
+    {
+      PyErr_SetString (PyExc_ValueError, "Invalid frame stop reason.");
+      return NULL;
+    }
+
+  str = frame_stop_reason_string (reason);
+  return PyUnicode_Decode (str, strlen (str), host_charset (), NULL);
+}
+
+/* Sets up the Frame API in the gdb module.  */
+
+void
+gdbpy_initialize_frames (void)
+{
+  if (PyType_Ready (&frame_object_type) < 0)
+    return;
+
+  /* Note: These would probably be best exposed as class attributes of Frame,
+     but I don't know how to do it except by messing with the type's dictionary.
+     That seems too messy.  */
+  PyModule_AddIntConstant (gdb_module, "NORMAL_FRAME", NORMAL_FRAME);
+  PyModule_AddIntConstant (gdb_module, "DUMMY_FRAME", DUMMY_FRAME);
+  PyModule_AddIntConstant (gdb_module, "SIGTRAMP_FRAME", SIGTRAMP_FRAME);
+  PyModule_AddIntConstant (gdb_module, "SENTINEL_FRAME", SENTINEL_FRAME);
+  PyModule_AddIntConstant (gdb_module,
+			   "FRAME_UNWIND_NO_REASON", UNWIND_NO_REASON);
+  PyModule_AddIntConstant (gdb_module,
+			   "FRAME_UNWIND_NULL_ID", UNWIND_NULL_ID);
+  PyModule_AddIntConstant (gdb_module,
+			   "FRAME_UNWIND_FIRST_ERROR", UNWIND_FIRST_ERROR);
+  PyModule_AddIntConstant (gdb_module,
+			   "FRAME_UNWIND_INNER_ID", UNWIND_INNER_ID);
+  PyModule_AddIntConstant (gdb_module,
+			   "FRAME_UNWIND_SAME_ID", UNWIND_SAME_ID);
+  PyModule_AddIntConstant (gdb_module,
+			   "FRAME_UNWIND_NO_SAVED_PC", UNWIND_NO_SAVED_PC);
+
+  Py_INCREF (&frame_object_type);
+  PyModule_AddObject (gdb_module, "Frame", (PyObject *) &frame_object_type);
+}
+
+\f
+
+static PyMethodDef frame_object_methods[] = {
+  { "equals", frapy_equal_p, METH_VARARGS,
+    "equals (frame) -> Boolean.\n\
+Compare this frame to the given frame." },
+  { "is_valid", frapy_is_valid, METH_NOARGS,
+    "is_valid () -> Boolean.\n\
+Return true if this frame is valid, false if not." },
+  { "name", frapy_name, METH_NOARGS,
+    "name () -> String.\n\
+Return the function name of the frame, or None if it can't be determined." },
+  { "type", frapy_type, METH_NOARGS,
+    "type () -> Integer.\n\
+Return the type of the frame." },
+  { "unwind_stop_reason", frapy_unwind_stop_reason, METH_NOARGS,
+    "unwind_stop_reason () -> Integer.\n\
+Return the reason why it's not possible to find frames older than this." },
+  { "pc", frapy_pc, METH_NOARGS,
+    "pc () -> Long.\n\
+Return the frame's resume address." },
+  { "older", frapy_older, METH_NOARGS,
+    "older () -> gdb.Frame.\n\
+Return the frame that called this frame." },
+  { "newer", frapy_newer, METH_NOARGS,
+    "newer () -> gdb.Frame.\n\
+Return the frame called by this frame." },
+  { "read_var", frapy_read_var, METH_VARARGS,
+    "read_var (variable) -> gdb.Value.\n\
+Return the value of the variable in this frame." },
+  {NULL}  /* Sentinel */
+};
+
+static PyTypeObject frame_object_type = {
+  PyObject_HEAD_INIT (NULL)
+  0,				  /* ob_size */
+  "gdb.Frame",			  /* tp_name */
+  sizeof (frame_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 */
+  frapy_str,			  /* tp_str */
+  0,				  /* tp_getattro */
+  0,				  /* tp_setattro */
+  0,				  /* tp_as_buffer */
+  Py_TPFLAGS_DEFAULT,		  /* tp_flags */
+  "GDB frame object",		  /* tp_doc */
+  0,				  /* tp_traverse */
+  0,				  /* tp_clear */
+  0,				  /* tp_richcompare */
+  0,				  /* tp_weaklistoffset */
+  0,				  /* tp_iter */
+  0,				  /* tp_iternext */
+  frame_object_methods,		  /* 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/python-internal.h b/gdb/python/python-internal.h
index 463f08e..f8d0896 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -64,12 +64,15 @@ extern PyObject *gdb_module;
 extern PyTypeObject value_object_type;
 
 PyObject *gdbpy_history (PyObject *self, PyObject *args);
+PyObject *gdbpy_frame_stop_reason_string (PyObject *, PyObject *);
+PyObject *gdbpy_selected_frame (PyObject *self, PyObject *args);
 
 PyObject *value_to_value_object (struct value *v);
 
 struct value *convert_value_from_python (PyObject *obj);
 
 void gdbpy_initialize_values (void);
+void gdbpy_initialize_frames (void);
 void gdbpy_initialize_commands (void);
 void gdbpy_initialize_functions (void);
 
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 29f83bb..02d97e3 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -412,6 +412,7 @@ Enables or disables printing of Python stack traces."),
   PyModule_AddStringConstant (gdb_module, "TARGET_CONFIG", (char*) target_name);
 
   gdbpy_initialize_values ();
+  gdbpy_initialize_frames ();
   gdbpy_initialize_commands ();
   gdbpy_initialize_functions ();
 
@@ -465,6 +466,13 @@ static PyMethodDef GdbMethods[] =
   { "get_parameter", get_parameter, METH_VARARGS,
     "Return a gdb parameter's value" },
 
+  { "selected_frame", gdbpy_selected_frame, METH_NOARGS,
+    "selected_frame () -> gdb.Frame.\n\
+Return the selected frame object." },
+  { "frame_stop_reason_string", gdbpy_frame_stop_reason_string, METH_VARARGS,
+    "stop_reason_string (Integer) -> String.\n\
+Return a string explaining unwind stop reason." },
+
   { "write", gdbpy_write, METH_VARARGS,
     "Write a string using gdb's filtered stream." },
   { "flush", gdbpy_flush, METH_NOARGS,
diff --git a/gdb/stack.c b/gdb/stack.c
index d0c872e..5e4fb4d 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -579,20 +579,16 @@ print_frame_info (struct frame_info *frame, int print_level,
   gdb_flush (gdb_stdout);
 }
 
-static void
-print_frame (struct frame_info *frame, int print_level,
-	     enum print_what print_what, int print_args,
-	     struct symtab_and_line sal)
+/* Attempt to obtain the FUNNAME and FUNLANG of the function corresponding
+   to FRAME.  */
+void
+find_frame_funname (struct frame_info *frame, char **funname,
+		    enum language *funlang)
 {
   struct symbol *func;
-  char *funname = NULL;
-  enum language funlang = language_unknown;
-  struct ui_stream *stb;
-  struct cleanup *old_chain, *list_chain;
-  struct value_print_options opts;
 
-  stb = ui_out_stream_new (uiout);
-  old_chain = make_cleanup_ui_out_stream_delete (stb);
+  *funname = NULL;
+  *funlang = language_unknown;
 
   func = find_pc_function (get_frame_address_in_block (frame));
   if (func)
@@ -625,24 +621,24 @@ print_frame (struct frame_info *frame, int print_level,
 	  /* We also don't know anything about the function besides
 	     its address and name.  */
 	  func = 0;
-	  funname = SYMBOL_PRINT_NAME (msymbol);
-	  funlang = SYMBOL_LANGUAGE (msymbol);
+	  *funname = SYMBOL_PRINT_NAME (msymbol);
+	  *funlang = SYMBOL_LANGUAGE (msymbol);
 	}
       else
 	{
-	  funname = SYMBOL_PRINT_NAME (func);
-	  funlang = SYMBOL_LANGUAGE (func);
-	  if (funlang == language_cplus)
+	  *funname = SYMBOL_PRINT_NAME (func);
+	  *funlang = SYMBOL_LANGUAGE (func);
+	  if (*funlang == language_cplus)
 	    {
 	      /* It seems appropriate to use SYMBOL_PRINT_NAME() here,
 		 to display the demangled name that we already have
 		 stored in the symbol table, but we stored a version
 		 with DMGL_PARAMS turned on, and here we don't want to
 		 display parameters.  So remove the parameters.  */
-	      char *func_only = cp_remove_params (funname);
+	      char *func_only = cp_remove_params (*funname);
 	      if (func_only)
 		{
-		  funname = func_only;
+		  *funname = func_only;
 		  make_cleanup (xfree, func_only);
 		}
 	    }
@@ -655,10 +651,27 @@ print_frame (struct frame_info *frame, int print_level,
 
       if (msymbol != NULL)
 	{
-	  funname = SYMBOL_PRINT_NAME (msymbol);
-	  funlang = SYMBOL_LANGUAGE (msymbol);
+	  *funname = SYMBOL_PRINT_NAME (msymbol);
+	  *funlang = SYMBOL_LANGUAGE (msymbol);
 	}
     }
+}
+
+static void
+print_frame (struct frame_info *frame, int print_level,
+	     enum print_what print_what, int print_args,
+	     struct symtab_and_line sal)
+{
+  char *funname = NULL;
+  enum language funlang = language_unknown;
+  struct ui_stream *stb;
+  struct cleanup *old_chain, *list_chain;
+  struct value_print_options opts;
+
+  stb = ui_out_stream_new (uiout);
+  old_chain = make_cleanup_ui_out_stream_delete (stb);
+
+  find_frame_funname (frame, &funname, &funlang);
 
   annotate_frame_begin (print_level ? frame_relative_level (frame) : 0,
 			get_frame_pc (frame));
@@ -694,7 +707,7 @@ print_frame (struct frame_info *frame, int print_level,
       struct print_args_args args;
       struct cleanup *args_list_chain;
       args.frame = frame;
-      args.func = func;
+      args.func = find_pc_function (get_frame_address_in_block (frame));
       args.stream = gdb_stdout;
       args_list_chain = make_cleanup_ui_out_list_begin_end (uiout, "args");
       catch_errors (print_args_stub, &args, "", RETURN_MASK_ERROR);
diff --git a/gdb/stack.h b/gdb/stack.h
index 973a57f..56b1d91 100644
--- a/gdb/stack.h
+++ b/gdb/stack.h
@@ -22,4 +22,9 @@
 
 void select_frame_command (char *level_exp, int from_tty);
 
+/* Attempt to obtain the FUNNAME and FUNLANG of the function corresponding
+   to FRAME.  */
+void find_frame_funname (struct frame_info *frame, char **funname,
+			 enum language *funlang);
+
 #endif /* #ifndef STACK_H */
diff --git a/gdb/testsuite/gdb.python/python-frame.c b/gdb/testsuite/gdb.python/python-frame.c
new file mode 100644
index 0000000..22eb9f2
--- /dev/null
+++ b/gdb/testsuite/gdb.python/python-frame.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-frame.exp b/gdb/testsuite/gdb.python/python-frame.exp
new file mode 100644
index 0000000..ce91074
--- /dev/null
+++ b/gdb/testsuite/gdb.python/python-frame.exp
@@ -0,0 +1,84 @@
+# 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 values to Python.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+set testfile "python-frame"
+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 f1 = gdb.selected_frame ()" "get second frame" 0
+gdb_py_test_silent_cmd "python f0 = f1.newer ()" "get first frame" 0
+
+gdb_test "python print 'result =', f0.equals (f1)" " = False" "test equals (false)"
+gdb_test "python print 'result =', f0.equals (f0)" " = True" "test equals (true)"
+gdb_test "python print 'result =', f0.is_valid ()" " = True" "test Frame.is_valid"
+gdb_test "python print 'result =', f0.name ()" " = f2" "test Frame.name"
+gdb_test "python print 'result =', f0.type () == gdb.NORMAL_FRAME" " = True" "test Frame.type"
+gdb_test "python print 'result =', f0.unwind_stop_reason () == gdb.FRAME_UNWIND_NO_REASON" " = True" "test Frame.type"
+gdb_test "python print 'result =', gdb.frame_stop_reason_string (gdb.FRAME_UNWIND_INNER_ID)" " = previous frame inner to this frame \\(corrupt stack\\?\\)" "test gdb.frame_stop_reason_string"
+gdb_test "python print 'result =', f0.pc ()" " = \[0-9\]+" "test Frame.pc"
+gdb_test "python print 'result =', f0.older ().equals (f1)" " = True" "test Frame.older"
+gdb_test "python print 'result =', f1.newer ().equals (f0)" " = True" "test Frame.newer"
+gdb_test "python print 'result =', f0.read_var ('b')" \
+  "ValueError: variable 'b' not found.*Error while executing Python code." \
+  "test Frame.read_var - error"
+gdb_test "python print 'result =', f0.read_var ('a')" " = 1" "test Frame.read_var - success"



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

* Re: [rfc] python API exposing inferior's frame stack.
  2009-03-20 23:04           ` Thiago Jung Bauermann
@ 2009-03-21  8:34             ` Eli Zaretskii
  2009-03-30  5:33               ` Thiago Jung Bauermann
  2009-03-22 16:00             ` Tom Tromey
  1 sibling, 1 reply; 19+ messages in thread
From: Eli Zaretskii @ 2009-03-21  8:34 UTC (permalink / raw)
  To: Thiago Jung Bauermann; +Cc: gdb-patches

> From: Thiago Jung Bauermann <bauerman@br.ibm.com>
> Cc: gdb-patches@sourceware.org
> Date: Fri, 20 Mar 2009 19:15:20 -0300
> 
> So I am changing the documentation and doc strings for Frame.older and
> Frame.newer to use similar wording:
> 
> +@defmethod Frame older
> +Return the frame that called this frame.
> +@end defmethod
> +
> +@defmethod Frame newer
> +Return the frame called by this frame.
> +@end defmethod
> 
> I also changed Frame.unwind_stop_reason to use "outermost":
> 
> +@defmethod Frame unwind_stop_reason
> +Return an integer representing the reason why it's not possible to find
> +more frames toward the outermost frame.  Use
> +@code{gdb.frame_stop_reason_string} to convert the value returned by this
> +function to a string.
> +@end defmethod
> 
> What do you think?

That's fine, thanks.

One comment:

> +@defmethod Frame equals frame
> +Compare frames.
> +@end defmethod

I see from the code that this return a boolean yes/no value (as
opposed to also providing some idea about the order, like strcmp in
C).  So perhaps "compare frames" might mislead someone.


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

* Re: [rfc] python API exposing inferior's frame stack.
  2009-03-20 23:04           ` Thiago Jung Bauermann
  2009-03-21  8:34             ` Eli Zaretskii
@ 2009-03-22 16:00             ` Tom Tromey
  1 sibling, 0 replies; 19+ messages in thread
From: Tom Tromey @ 2009-03-22 16:00 UTC (permalink / raw)
  To: Thiago Jung Bauermann; +Cc: Eli Zaretskii, gdb-patches

>>>>> "Thiago" == Thiago Jung Bauermann <bauerman@br.ibm.com> writes:

Thiago> +static PyMethodDef frame_object_methods[] = {
Thiago> +  { "equals", frapy_equal_p, METH_VARARGS,
Thiago> +    "equals (frame) -> Boolean.\n\
Thiago> +Compare this frame to the given frame." },

I think we should probably use tp_richcompare, rather than an explicit
method.

Tom


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

* Re: [rfc] python API exposing inferior's frame stack.
  2009-03-21  8:34             ` Eli Zaretskii
@ 2009-03-30  5:33               ` Thiago Jung Bauermann
  2009-03-30 18:35                 ` Eli Zaretskii
  2009-03-30 19:09                 ` Tom Tromey
  0 siblings, 2 replies; 19+ messages in thread
From: Thiago Jung Bauermann @ 2009-03-30  5:33 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches, Tom Tromey

El sáb, 21-03-2009 a las 10:03 +0200, Eli Zaretskii escribió:
> > From: Thiago Jung Bauermann <bauerman@br.ibm.com>
> > Cc: gdb-patches@sourceware.org
> > Date: Fri, 20 Mar 2009 19:15:20 -0300
> > 
> One comment:
> 
> > +@defmethod Frame equals frame
> > +Compare frames.
> > +@end defmethod
> 
> I see from the code that this return a boolean yes/no value (as
> opposed to also providing some idea about the order, like strcmp in
> C).  So perhaps "compare frames" might mislead someone.

Tromey suggested implementing the == operator for Frame objects, instead
of adding an equals method. I implemented that, so the documentation
excerpt above doesn't exist anymore. I added a sentence mentioning that
you can compare Frame objects with ==.

Ok to commit?
-- 
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center


gdb/
2009-03-30  Thiago Jung Bauermann  <bauerman@br.ibm.com>

	Expose frames to Python.
	* Makefile.in (SUBDIR_PYTHON_OBS): Add python-frame.o.
	(SUBDIR_PYTHON_SRCS): Add python-frame.c.
	(python-frame.o): New target.
	* python/python-frame.c: New file.
	* python/python-internal.h (gdbpy_frames, gdbpy_newest_frame,
	gdbpy_frame_stop_reason_string, gdbpy_selected_frame,
	gdbpy_initialize_frames): New prototypes.
	* python/python.c (_initialize_python): Call gdbpy_initialize_frames.
	(GdbMethods): Add `selected_frame' and `frame_stop_reason_string'
	entries.
	* stack.c (find_frame_funname): New function, factored out of
	print_frame.
	(print_frame): Call find_frame_funname.
	* stack.h (find_frame_funname): Add prototype.

gdb/doc/
2009-03-30  Thiago Jung Bauermann  <bauerman@br.ibm.com>

	* gdb.texinfo (Frames in Python): New node.
	(Python API): Update.

gdb/testsuite/
2009-03-30  Thiago Jung Bauermann  <bauerman@br.ibm.com>

	* gdb.python/python-frame.c: New file.
	* gdb.python/python-frame.exp: New file.

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 6b69881..2b5e0ac 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -266,12 +266,14 @@ SUBDIR_TUI_CFLAGS= \
 SUBDIR_PYTHON_OBS = \
 	python.o \
 	python-cmd.o \
+	python-frame.o \
 	python-function.o \
 	python-utils.o \
 	python-value.o
 SUBDIR_PYTHON_SRCS = \
 	python/python.c \
 	python/python-cmd.c \
+	python/python-frame.c \
 	python/python-function.c \
 	python/python-utils.c \
 	python/python-value.c
@@ -1847,6 +1849,10 @@ python-cmd.o: $(srcdir)/python/python-cmd.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-cmd.c
 	$(POSTCOMPILE)
 
+python-frame.o: $(srcdir)/python/python-frame.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-frame.c
+	$(POSTCOMPILE)
+
 python-function.o: $(srcdir)/python/python-function.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-function.c
 	$(POSTCOMPILE)
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 166039b..431f3ae 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -18194,6 +18194,7 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
 * Values From Inferior::
 * Commands In Python::          Implementing new commands in Python.
 * Functions In Python::         Writing new convenience functions.
+* Frames In Python::            Acessing inferior stack frames from Python.
 @end menu
 
 @node Basic Python
@@ -18702,6 +18703,84 @@ registration of the function with @value{GDBN}.  Depending on how the
 Python code is read into @value{GDBN}, you may need to import the
 @code{gdb} module explicitly.
 
+@node Frames In Python
+@subsubsection Acessing inferior stack frames from Python.
+
+@cindex frames in python
+When the debugged program stops, @value{GDBN} is able to analyze its call
+stack (@pxref{Frames,,Stack frames}).  The @code{gdb.Frame} class
+represents a frame in the stack.  A @code{gdb.Frame} object is only valid
+while its corresponding frame exists in the inferior's stack.  If you try
+to use an invalid frame object, @value{GDBN} will throw a @code{RuntimeError}
+exception.
+
+Two @code{gdb.Frame} objects can be compared for equality with the @code{==}
+operator, like:
+
+@smallexample
+(@value{GDBP}) python print gdb.newest_frame() == gdb.selected_frame ()
+True
+@end smallexample
+
+The following frame-related functions are available in the @code{gdb} module:
+
+@findex gdb.selected_frame
+@defun selected_frame
+Return the selected frame object.  (@pxref{Selection,,Selecting a Frame}).
+@end defun
+
+@defun frame_stop_reason_string reason
+Return a string explaining the reason why @value{GDBN} stopped unwinding
+frames, as expressed by the given @var{reason} code (an integer, see the
+@code{unwind_stop_reason} method further down in this section).
+@end defun
+
+A @code{gdb.Frame} object has the following methods:
+
+@table @code
+@defmethod Frame is_valid
+Returns true if the @code{gdb.Frame} object is valid, false if not.
+A frame object can become invalid if the frame it refers to doesn't
+exist anymore in the inferior.  All @code{gdb.Frame} methods will throw
+an exception if it is invalid at the time the method is called.
+@end defmethod
+
+@defmethod Frame name
+Returns the function name of the frame, or @code{None} if it can't be
+obtained.
+@end defmethod
+
+@defmethod Frame type
+Returns the type of the frame.  The value can be one of
+@code{gdb.NORMAL_FRAME}, @code{gdb.DUMMY_FRAME}, @code{gdb.SIGTRAMP_FRAME}
+or @code{gdb.SENTINEL_FRAME}.
+@end defmethod
+
+@defmethod Frame unwind_stop_reason
+Return an integer representing the reason why it's not possible to find
+more frames toward the outermost frame.  Use
+@code{gdb.frame_stop_reason_string} to convert the value returned by this
+function to a string.
+@end defmethod
+
+@defmethod Frame pc
+Returns the frame's resume address.
+@end defmethod
+
+@defmethod Frame older
+Return the frame that called this frame.
+@end defmethod
+
+@defmethod Frame newer
+Return the frame called by this frame.
+@end defmethod
+
+@defmethod Frame read_var variable
+Return the value of the given variable in this frame.  @var{variable} must
+be a string.
+@end defmethod
+@end table
+
 @node Interpreters
 @chapter Command Interpreters
 @cindex command interpreters
diff --git a/gdb/python/python-frame.c b/gdb/python/python-frame.c
new file mode 100644
index 0000000..fc5422f
--- /dev/null
+++ b/gdb/python/python-frame.c
@@ -0,0 +1,538 @@
+/* Python interface to stack frames
+
+   Copyright (C) 2008, 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 "charset.h"
+#include "block.h"
+#include "frame.h"
+#include "exceptions.h"
+#include "symtab.h"
+#include "stack.h"
+#include "value.h"
+#include "python-internal.h"
+
+typedef struct {
+  PyObject_HEAD
+  struct frame_id frame_id;
+  struct gdbarch *gdbarch;
+
+  /* Marks that the FRAME_ID member actually holds the ID of the frame next
+     to this, and not this frames' ID itself.  This is a hack to permit Python
+     frame objects which represent invalid frames (i.e., the last frame_info
+     in a corrupt stack).  The problem arises from the fact that this code
+     relies on FRAME_ID to uniquely identify a frame, which is not always true
+     for the last "frame" in a corrupt stack (it can have a null ID, or the same
+     ID as the  previous frame).  Whenever get_prev_frame returns NULL, we
+     record the frame_id of the next frame and set FRAME_ID_IS_NEXT to 1.  */
+  int frame_id_is_next;
+} frame_object;
+
+/* Require a valid frame.  This must be called inside a TRY_CATCH, or
+   another context in which a gdb exception is allowed.  */
+#define FRAPY_REQUIRE_VALID(frame_obj, frame)		\
+    do {						\
+      frame = frame_object_to_frame_info (frame_obj);	\
+      if (frame == NULL)				\
+	error ("Frame is invalid.");			\
+    } while (0)
+
+static PyTypeObject frame_object_type;
+
+/* Returns the frame_info object corresponding to the given Python Frame
+   object.  If the frame doesn't exist anymore (the frame id doesn't
+   correspond to any frame in the inferior), returns NULL.  */
+
+static struct frame_info *
+frame_object_to_frame_info (frame_object *frame_obj)
+{
+  struct frame_info *frame;
+
+  frame = frame_find_by_id (frame_obj->frame_id);
+  if (frame == NULL)
+    return NULL;
+
+  if (frame_obj->frame_id_is_next)
+    frame = get_prev_frame (frame);
+
+  return frame;
+}
+
+/* Called by the Python interpreter to obtain string representation
+   of the object.  */
+
+static PyObject *
+frapy_str (PyObject *self)
+{
+  char *s;
+  long len;
+  PyObject *result;
+  struct ui_file *strfile;
+
+  strfile = mem_fileopen ();
+  fprint_frame_id (strfile, ((frame_object *) self)->frame_id);
+  s = ui_file_xstrdup (strfile, &len);
+  result = PyString_FromString (s);
+  xfree (s);
+
+  return result;
+}
+
+/* Implementation of gdb.Frame.is_valid (self) -> Boolean.
+   Returns True if the frame corresponding to the frame_id of this
+   object still exists in the inferior.  */
+
+static PyObject *
+frapy_is_valid (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame;
+
+  frame = frame_object_to_frame_info ((frame_object *) self);
+  if (frame == NULL)
+    Py_RETURN_FALSE;
+
+  Py_RETURN_TRUE;
+}
+
+/* Implementation of gdb.Frame.name (self) -> String.
+   Returns the name of the function corresponding to this frame.  */
+
+static PyObject *
+frapy_name (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame;
+  char *name;
+  enum language lang;
+  PyObject *result;
+  volatile struct gdb_exception except;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+      find_frame_funname (frame, &name, &lang);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  if (name)
+    result = target_string_to_unicode (name, strlen (name));
+  else
+    {
+      result = Py_None;
+      Py_INCREF (Py_None);
+    }
+
+  return result;
+}
+
+/* Implementation of gdb.Frame.type (self) -> Integer.
+   Returns the frame type, namely one of the gdb.*_FRAME constants.  */
+
+static PyObject *
+frapy_type (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame;
+  enum frame_type type = NORMAL_FRAME;/* Initialize to appease gcc warning.  */
+  volatile struct gdb_exception except;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+      type = get_frame_type (frame);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  return PyInt_FromLong (type);
+}
+
+/* Implementation of gdb.Frame.unwind_stop_reason (self) -> Integer.
+   Returns one of the gdb.FRAME_UNWIND_* constants.  */
+
+static PyObject *
+frapy_unwind_stop_reason (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame = NULL;    /* Initialize to appease gcc warning.  */
+  volatile struct gdb_exception except;
+  enum unwind_stop_reason stop_reason;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  stop_reason = get_frame_unwind_stop_reason (frame);
+
+  return PyInt_FromLong (stop_reason);
+}
+
+/* Implementation of gdb.Frame.pc (self) -> Long.
+   Returns the frame's resume address.  */
+
+static PyObject *
+frapy_pc (PyObject *self, PyObject *args)
+{
+  CORE_ADDR pc = 0;	      /* Initialize to appease gcc warning.  */
+  struct frame_info *frame;
+  volatile struct gdb_exception except;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+      pc = get_frame_pc (frame);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  return PyLong_FromUnsignedLongLong (pc);
+}
+
+/* Convert a frame_info struct to a Python Frame object.
+   Sets a Python exception and returns NULL on error.  */
+
+static frame_object *
+frame_info_to_frame_object (struct frame_info *frame)
+{
+  frame_object *frame_obj;
+
+  frame_obj = PyObject_New (frame_object, &frame_object_type);
+  if (frame_obj == NULL)
+    {
+      PyErr_SetString (PyExc_MemoryError, "Could not allocate frame object.");
+      return NULL;
+    }
+
+  /* Try to get the previous frame, to determine if this is the last frame
+     in a corrupt stack.  If so, we need to store the frame_id of the next
+     frame and not of this one (which is possibly invalid).  */
+  if (get_prev_frame (frame) == NULL
+      && get_frame_unwind_stop_reason (frame) != UNWIND_NO_REASON
+      && get_next_frame (frame) != NULL)
+    {
+      frame_obj->frame_id = get_frame_id (get_next_frame (frame));
+      frame_obj->frame_id_is_next = 1;
+    }
+  else
+    {
+      frame_obj->frame_id = get_frame_id (frame);
+      frame_obj->frame_id_is_next = 0;
+    }
+
+  frame_obj->gdbarch = get_frame_arch (frame);
+
+  return frame_obj;
+}
+
+/* Implementation of gdb.Frame.older (self) -> gdb.Frame.
+   Returns the frame immediately older (outer) to this frame, or None if
+   there isn't one.  */
+
+static PyObject *
+frapy_older (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame, *prev;
+  volatile struct gdb_exception except;
+  PyObject *prev_obj = NULL;   /* Initialize to appease gcc warning.  */
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+      prev = get_prev_frame (frame);
+      if (prev)
+	prev_obj = (PyObject *) frame_info_to_frame_object (prev);
+      else
+	{
+	  Py_INCREF (Py_None);
+	  prev_obj = Py_None;
+	}
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  return prev_obj;
+}
+
+/* Implementation of gdb.Frame.newer (self) -> gdb.Frame.
+   Returns the frame immediately newer (inner) to this frame, or None if
+   there isn't one.  */
+
+static PyObject *
+frapy_newer (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame, *next;
+  volatile struct gdb_exception except;
+  PyObject *next_obj = NULL;   /* Initialize to appease gcc warning.  */
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+      next = get_next_frame (frame);
+      if (next)
+	next_obj = (PyObject *) frame_info_to_frame_object (next);
+      else
+	{
+	  Py_INCREF (Py_None);
+	  next_obj = Py_None;
+	}
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  return next_obj;
+}
+
+/* Implementation of gdb.Frame.read_var_value (self, variable) -> gdb.Value.
+   Returns the value of the given variable in this frame.  The argument must be
+   a string.  Returns None if GDB can't find the specified variable.  */
+
+static PyObject *
+frapy_read_var (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame;
+  PyObject *sym_obj;
+  struct symbol *var = NULL;	/* gcc-4.3.2 false warning.  */
+  struct value *val = NULL;
+  volatile struct gdb_exception except;
+
+  if (!PyArg_ParseTuple (args, "O", &sym_obj))
+    return NULL;
+
+  if (gdbpy_is_string (sym_obj))
+    {
+      char *var_name;
+      struct block *block = NULL;
+      struct cleanup *cleanup;
+      volatile struct gdb_exception except;
+
+      var_name = python_string_to_target_string (sym_obj);
+      if (!var_name)
+	return NULL;
+      cleanup = make_cleanup (xfree, var_name);
+
+      TRY_CATCH (except, RETURN_MASK_ALL)
+	{
+	  FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+	  block = block_for_pc (get_frame_address_in_block (frame));
+	  var = lookup_symbol (var_name, block, VAR_DOMAIN, NULL);
+	}
+      GDB_PY_HANDLE_EXCEPTION (except);
+
+      if (!var)
+	{
+	  PyErr_Format (PyExc_ValueError,
+			_("variable '%s' not found"), var_name);
+	  do_cleanups (cleanup);
+
+	  return NULL;
+	}
+
+      do_cleanups (cleanup);
+    }
+  else
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       _("argument must be a symbol or string"));
+      return NULL;
+    }
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+      val = read_var_value (var, frame);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  if (val)
+    return value_to_value_object (val);
+
+  Py_RETURN_NONE;
+}
+
+/* Implementation of gdb.selected_frame () -> gdb.Frame.
+   Returns the selected frame object.  */
+
+PyObject *
+gdbpy_selected_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_selected_frame ("No frame is currently selected.");
+      frame_obj = frame_info_to_frame_object (frame);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  return (PyObject *) frame_obj;
+}
+
+/* Implementation of gdb.stop_reason_string (Integer) -> String.
+   Return a string explaining the unwind stop reason.  */
+
+PyObject *
+gdbpy_frame_stop_reason_string (PyObject *self, PyObject *args)
+{
+  int reason;
+  const char *str;
+
+  if (!PyArg_ParseTuple (args, "i", &reason))
+    return NULL;
+
+  if (reason < 0 || reason > UNWIND_NO_SAVED_PC)
+    {
+      PyErr_SetString (PyExc_ValueError, "Invalid frame stop reason.");
+      return NULL;
+    }
+
+  str = frame_stop_reason_string (reason);
+  return PyUnicode_Decode (str, strlen (str), host_charset (), NULL);
+}
+
+/* Implements the equality comparison for Frame objects.
+   All other comparison operators will throw a TypeError Python exception,
+   as they aren't valid for frames.  */
+
+static PyObject *
+frapy_richcompare (PyObject *self, PyObject *other, int op)
+{
+  if (!PyObject_TypeCheck (other, &frame_object_type))
+    {
+      PyErr_SetString (PyExc_TypeError, "Frame object expected in comparison.");
+      return NULL;
+    }
+  else if (op != Py_EQ)
+    {
+      PyErr_SetString (PyExc_TypeError, "Invalid comparison for gdb.Frame.");
+      return NULL;
+    }
+
+  if (frame_id_eq (((frame_object *) self)->frame_id,
+		   ((frame_object *) other)->frame_id))
+    Py_RETURN_TRUE;
+
+  Py_RETURN_FALSE;
+}
+
+/* Sets up the Frame API in the gdb module.  */
+
+void
+gdbpy_initialize_frames (void)
+{
+  if (PyType_Ready (&frame_object_type) < 0)
+    return;
+
+  /* Note: These would probably be best exposed as class attributes of Frame,
+     but I don't know how to do it except by messing with the type's dictionary.
+     That seems too messy.  */
+  PyModule_AddIntConstant (gdb_module, "NORMAL_FRAME", NORMAL_FRAME);
+  PyModule_AddIntConstant (gdb_module, "DUMMY_FRAME", DUMMY_FRAME);
+  PyModule_AddIntConstant (gdb_module, "SIGTRAMP_FRAME", SIGTRAMP_FRAME);
+  PyModule_AddIntConstant (gdb_module, "SENTINEL_FRAME", SENTINEL_FRAME);
+  PyModule_AddIntConstant (gdb_module,
+			   "FRAME_UNWIND_NO_REASON", UNWIND_NO_REASON);
+  PyModule_AddIntConstant (gdb_module,
+			   "FRAME_UNWIND_NULL_ID", UNWIND_NULL_ID);
+  PyModule_AddIntConstant (gdb_module,
+			   "FRAME_UNWIND_FIRST_ERROR", UNWIND_FIRST_ERROR);
+  PyModule_AddIntConstant (gdb_module,
+			   "FRAME_UNWIND_INNER_ID", UNWIND_INNER_ID);
+  PyModule_AddIntConstant (gdb_module,
+			   "FRAME_UNWIND_SAME_ID", UNWIND_SAME_ID);
+  PyModule_AddIntConstant (gdb_module,
+			   "FRAME_UNWIND_NO_SAVED_PC", UNWIND_NO_SAVED_PC);
+
+  Py_INCREF (&frame_object_type);
+  PyModule_AddObject (gdb_module, "Frame", (PyObject *) &frame_object_type);
+}
+
+\f
+
+static PyMethodDef frame_object_methods[] = {
+  { "is_valid", frapy_is_valid, METH_NOARGS,
+    "is_valid () -> Boolean.\n\
+Return true if this frame is valid, false if not." },
+  { "name", frapy_name, METH_NOARGS,
+    "name () -> String.\n\
+Return the function name of the frame, or None if it can't be determined." },
+  { "type", frapy_type, METH_NOARGS,
+    "type () -> Integer.\n\
+Return the type of the frame." },
+  { "unwind_stop_reason", frapy_unwind_stop_reason, METH_NOARGS,
+    "unwind_stop_reason () -> Integer.\n\
+Return the reason why it's not possible to find frames older than this." },
+  { "pc", frapy_pc, METH_NOARGS,
+    "pc () -> Long.\n\
+Return the frame's resume address." },
+  { "older", frapy_older, METH_NOARGS,
+    "older () -> gdb.Frame.\n\
+Return the frame that called this frame." },
+  { "newer", frapy_newer, METH_NOARGS,
+    "newer () -> gdb.Frame.\n\
+Return the frame called by this frame." },
+  { "read_var", frapy_read_var, METH_VARARGS,
+    "read_var (variable) -> gdb.Value.\n\
+Return the value of the variable in this frame." },
+  {NULL}  /* Sentinel */
+};
+
+static PyTypeObject frame_object_type = {
+  PyObject_HEAD_INIT (NULL)
+  0,				  /* ob_size */
+  "gdb.Frame",			  /* tp_name */
+  sizeof (frame_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 */
+  frapy_str,			  /* tp_str */
+  0,				  /* tp_getattro */
+  0,				  /* tp_setattro */
+  0,				  /* tp_as_buffer */
+  Py_TPFLAGS_DEFAULT,		  /* tp_flags */
+  "GDB frame object",		  /* tp_doc */
+  0,				  /* tp_traverse */
+  0,				  /* tp_clear */
+  frapy_richcompare,		  /* tp_richcompare */
+  0,				  /* tp_weaklistoffset */
+  0,				  /* tp_iter */
+  0,				  /* tp_iternext */
+  frame_object_methods,		  /* 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/python-internal.h b/gdb/python/python-internal.h
index 463f08e..f8d0896 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -64,12 +64,15 @@ extern PyObject *gdb_module;
 extern PyTypeObject value_object_type;
 
 PyObject *gdbpy_history (PyObject *self, PyObject *args);
+PyObject *gdbpy_frame_stop_reason_string (PyObject *, PyObject *);
+PyObject *gdbpy_selected_frame (PyObject *self, PyObject *args);
 
 PyObject *value_to_value_object (struct value *v);
 
 struct value *convert_value_from_python (PyObject *obj);
 
 void gdbpy_initialize_values (void);
+void gdbpy_initialize_frames (void);
 void gdbpy_initialize_commands (void);
 void gdbpy_initialize_functions (void);
 
diff --git a/gdb/python/python.c b/gdb/python/python.c
index b48cf05..52fc780 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -411,6 +411,7 @@ Enables or disables printing of Python stack traces."),
   PyModule_AddStringConstant (gdb_module, "TARGET_CONFIG", (char*) target_name);
 
   gdbpy_initialize_values ();
+  gdbpy_initialize_frames ();
   gdbpy_initialize_commands ();
   gdbpy_initialize_functions ();
 
@@ -464,6 +465,13 @@ static PyMethodDef GdbMethods[] =
   { "get_parameter", get_parameter, METH_VARARGS,
     "Return a gdb parameter's value" },
 
+  { "selected_frame", gdbpy_selected_frame, METH_NOARGS,
+    "selected_frame () -> gdb.Frame.\n\
+Return the selected frame object." },
+  { "frame_stop_reason_string", gdbpy_frame_stop_reason_string, METH_VARARGS,
+    "stop_reason_string (Integer) -> String.\n\
+Return a string explaining unwind stop reason." },
+
   { "write", gdbpy_write, METH_VARARGS,
     "Write a string using gdb's filtered stream." },
   { "flush", gdbpy_flush, METH_NOARGS,
diff --git a/gdb/stack.c b/gdb/stack.c
index bf9e576..7e31394 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -644,20 +644,16 @@ print_frame_info (struct frame_info *frame, int print_level,
   gdb_flush (gdb_stdout);
 }
 
-static void
-print_frame (struct frame_info *frame, int print_level,
-	     enum print_what print_what, int print_args,
-	     struct symtab_and_line sal)
+/* Attempt to obtain the FUNNAME and FUNLANG of the function corresponding
+   to FRAME.  */
+void
+find_frame_funname (struct frame_info *frame, char **funname,
+		    enum language *funlang)
 {
   struct symbol *func;
-  char *funname = NULL;
-  enum language funlang = language_unknown;
-  struct ui_stream *stb;
-  struct cleanup *old_chain, *list_chain;
-  struct value_print_options opts;
 
-  stb = ui_out_stream_new (uiout);
-  old_chain = make_cleanup_ui_out_stream_delete (stb);
+  *funname = NULL;
+  *funlang = language_unknown;
 
   func = find_pc_function (get_frame_address_in_block (frame));
   if (func)
@@ -690,24 +686,24 @@ print_frame (struct frame_info *frame, int print_level,
 	  /* We also don't know anything about the function besides
 	     its address and name.  */
 	  func = 0;
-	  funname = SYMBOL_PRINT_NAME (msymbol);
-	  funlang = SYMBOL_LANGUAGE (msymbol);
+	  *funname = SYMBOL_PRINT_NAME (msymbol);
+	  *funlang = SYMBOL_LANGUAGE (msymbol);
 	}
       else
 	{
-	  funname = SYMBOL_PRINT_NAME (func);
-	  funlang = SYMBOL_LANGUAGE (func);
-	  if (funlang == language_cplus)
+	  *funname = SYMBOL_PRINT_NAME (func);
+	  *funlang = SYMBOL_LANGUAGE (func);
+	  if (*funlang == language_cplus)
 	    {
 	      /* It seems appropriate to use SYMBOL_PRINT_NAME() here,
 		 to display the demangled name that we already have
 		 stored in the symbol table, but we stored a version
 		 with DMGL_PARAMS turned on, and here we don't want to
 		 display parameters.  So remove the parameters.  */
-	      char *func_only = cp_remove_params (funname);
+	      char *func_only = cp_remove_params (*funname);
 	      if (func_only)
 		{
-		  funname = func_only;
+		  *funname = func_only;
 		  make_cleanup (xfree, func_only);
 		}
 	    }
@@ -720,10 +716,27 @@ print_frame (struct frame_info *frame, int print_level,
 
       if (msymbol != NULL)
 	{
-	  funname = SYMBOL_PRINT_NAME (msymbol);
-	  funlang = SYMBOL_LANGUAGE (msymbol);
+	  *funname = SYMBOL_PRINT_NAME (msymbol);
+	  *funlang = SYMBOL_LANGUAGE (msymbol);
 	}
     }
+}
+
+static void
+print_frame (struct frame_info *frame, int print_level,
+	     enum print_what print_what, int print_args,
+	     struct symtab_and_line sal)
+{
+  char *funname = NULL;
+  enum language funlang = language_unknown;
+  struct ui_stream *stb;
+  struct cleanup *old_chain, *list_chain;
+  struct value_print_options opts;
+
+  stb = ui_out_stream_new (uiout);
+  old_chain = make_cleanup_ui_out_stream_delete (stb);
+
+  find_frame_funname (frame, &funname, &funlang);
 
   annotate_frame_begin (print_level ? frame_relative_level (frame) : 0,
 			get_frame_pc (frame));
@@ -759,7 +772,7 @@ print_frame (struct frame_info *frame, int print_level,
       struct print_args_args args;
       struct cleanup *args_list_chain;
       args.frame = frame;
-      args.func = func;
+      args.func = find_pc_function (get_frame_address_in_block (frame));
       args.stream = gdb_stdout;
       args_list_chain = make_cleanup_ui_out_list_begin_end (uiout, "args");
       catch_errors (print_args_stub, &args, "", RETURN_MASK_ERROR);
diff --git a/gdb/stack.h b/gdb/stack.h
index 973a57f..56b1d91 100644
--- a/gdb/stack.h
+++ b/gdb/stack.h
@@ -22,4 +22,9 @@
 
 void select_frame_command (char *level_exp, int from_tty);
 
+/* Attempt to obtain the FUNNAME and FUNLANG of the function corresponding
+   to FRAME.  */
+void find_frame_funname (struct frame_info *frame, char **funname,
+			 enum language *funlang);
+
 #endif /* #ifndef STACK_H */
diff --git a/gdb/testsuite/gdb.python/python-frame.c b/gdb/testsuite/gdb.python/python-frame.c
new file mode 100644
index 0000000..22eb9f2
--- /dev/null
+++ b/gdb/testsuite/gdb.python/python-frame.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-frame.exp b/gdb/testsuite/gdb.python/python-frame.exp
new file mode 100644
index 0000000..b1ee9be
--- /dev/null
+++ b/gdb/testsuite/gdb.python/python-frame.exp
@@ -0,0 +1,86 @@
+# 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 values to Python.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+set testfile "python-frame"
+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 f1 = gdb.selected_frame ()" "get second frame" 0
+gdb_py_test_silent_cmd "python f0 = f1.newer ()" "get first frame" 0
+
+gdb_test "python print 'result =', f0 == f1" " = False" "test equality comparison (false)"
+gdb_test "python print 'result =', f0 == f0" " = True" "test equality comparison (true)"
+gdb_test "python print 'result =', f0.is_valid ()" " = True" "test Frame.is_valid"
+gdb_test "python print 'result =', f0.name ()" " = f2" "test Frame.name"
+gdb_test "python print 'result =', f0.type () == gdb.NORMAL_FRAME" " = True" "test Frame.type"
+gdb_test "python print 'result =', f0.unwind_stop_reason () == gdb.FRAME_UNWIND_NO_REASON" " = True" "test Frame.type"
+gdb_test "python print 'result =', gdb.frame_stop_reason_string (gdb.FRAME_UNWIND_INNER_ID)" " = previous frame inner to this frame \\(corrupt stack\\?\\)" "test gdb.frame_stop_reason_string"
+gdb_test "python print 'result =', f0.pc ()" " = \[0-9\]+" "test Frame.pc"
+gdb_test "python print 'result =', f0.older () == f1" " = True" "test Frame.older"
+gdb_test "python print 'result =', f1.newer () == f0" " = True" "test Frame.newer"
+gdb_test "python print 'result =', f0.read_var ('variable_which_surely_doesnt_exist')" \
+  "ValueError: variable 'variable_which_surely_doesnt_exist' not found.*Error while executing Python code." \
+  "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.selected_frame () == f1" " = True" "test gdb.selected_frame"



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

* Re: [rfc] python API exposing inferior's frame stack.
  2009-03-30  5:33               ` Thiago Jung Bauermann
@ 2009-03-30 18:35                 ` Eli Zaretskii
  2009-03-30 19:09                 ` Tom Tromey
  1 sibling, 0 replies; 19+ messages in thread
From: Eli Zaretskii @ 2009-03-30 18:35 UTC (permalink / raw)
  To: Thiago Jung Bauermann; +Cc: gdb-patches, tromey

> From: Thiago Jung Bauermann <bauerman@br.ibm.com>
> Cc: gdb-patches@sourceware.org, Tom Tromey <tromey@redhat.com>
> Date: Mon, 30 Mar 2009 00:51:54 -0300
> 
> Tromey suggested implementing the == operator for Frame objects, instead
> of adding an equals method. I implemented that, so the documentation
> excerpt above doesn't exist anymore. I added a sentence mentioning that
> you can compare Frame objects with ==.
> 
> Ok to commit?

Yes, thanks.


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

* Re: [rfc] python API exposing inferior's frame stack.
  2009-03-30  5:33               ` Thiago Jung Bauermann
  2009-03-30 18:35                 ` Eli Zaretskii
@ 2009-03-30 19:09                 ` Tom Tromey
  2009-03-30 19:24                   ` Thiago Jung Bauermann
  1 sibling, 1 reply; 19+ messages in thread
From: Tom Tromey @ 2009-03-30 19:09 UTC (permalink / raw)
  To: Thiago Jung Bauermann; +Cc: Eli Zaretskii, gdb-patches

>>>>> "Thiago" == Thiago Jung Bauermann <bauerman@br.ibm.com> writes:

Thiago> 2009-03-30  Thiago Jung Bauermann  <bauerman@br.ibm.com>
Thiago> 	Expose frames to Python.
[...]

Thiago> +static PyObject *
Thiago> +frapy_name (PyObject *self, PyObject *args)
[...]

Thiago> +  if (name)
Thiago> +    result = target_string_to_unicode (name, strlen (name));

I think symbols are assumed to use the host encoding.

Ok with this fix.

Tom


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

* Re: [rfc] python API exposing inferior's frame stack.
  2009-03-30 19:09                 ` Tom Tromey
@ 2009-03-30 19:24                   ` Thiago Jung Bauermann
  2009-03-30 19:57                     ` Tom Tromey
  0 siblings, 1 reply; 19+ messages in thread
From: Thiago Jung Bauermann @ 2009-03-30 19:24 UTC (permalink / raw)
  To: tromey; +Cc: Eli Zaretskii, gdb-patches

El lun, 30-03-2009 a las 13:07 -0600, Tom Tromey escribió:
> >>>>> "Thiago" == Thiago Jung Bauermann <bauerman@br.ibm.com> writes:
> Thiago> +static PyObject *
> Thiago> +frapy_name (PyObject *self, PyObject *args)
> [...]
> 
> Thiago> +  if (name)
> Thiago> +    result = target_string_to_unicode (name, strlen (name));
> 
> I think symbols are assumed to use the host encoding.

Are they? This really surprises me, since symbols are obtained from the
executable file. Or the symbol reading mechanism converts them from
target to host encoding?
-- 
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center


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

* Re: [rfc] python API exposing inferior's frame stack.
  2009-03-30 19:24                   ` Thiago Jung Bauermann
@ 2009-03-30 19:57                     ` Tom Tromey
  2009-03-30 20:50                       ` Thiago Jung Bauermann
  0 siblings, 1 reply; 19+ messages in thread
From: Tom Tromey @ 2009-03-30 19:57 UTC (permalink / raw)
  To: Thiago Jung Bauermann; +Cc: Eli Zaretskii, gdb-patches

>>>>> "Thiago" == Thiago Jung Bauermann <bauerman@br.ibm.com> writes:

Tom> I think symbols are assumed to use the host encoding.

Thiago> Are they? This really surprises me, since symbols are obtained from the
Thiago> executable file. Or the symbol reading mechanism converts them from
Thiago> target to host encoding?

I think this is a bit of a weird area in gdb.  From what I can tell,
symbol readers don't convert between encodings.  So, I think if
symbols are actually encoded in some other encoding, we'll do the
wrong thing eventually.

Anyway, gdb definitely does currently assume that symbols are in the
host encoding.  E.g., the other caller of find_frame_funname calls
fprintf_symbol_filtered, which may print the argument string directly.

Tom


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

* Re: [rfc] python API exposing inferior's frame stack.
  2009-03-30 19:57                     ` Tom Tromey
@ 2009-03-30 20:50                       ` Thiago Jung Bauermann
  0 siblings, 0 replies; 19+ messages in thread
From: Thiago Jung Bauermann @ 2009-03-30 20:50 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Eli Zaretskii, gdb-patches

El lun, 30-03-2009 a las 13:24 -0600, Tom Tromey escribió:
> >>>>> "Thiago" == Thiago Jung Bauermann <bauerman@br.ibm.com> writes:
> 
> Tom> I think symbols are assumed to use the host encoding.
> 
> Thiago> Are they? This really surprises me, since symbols are obtained from the
> Thiago> executable file. Or the symbol reading mechanism converts them from
> Thiago> target to host encoding?
> 
> I think this is a bit of a weird area in gdb.  From what I can tell,
> symbol readers don't convert between encodings.  So, I think if
> symbols are actually encoded in some other encoding, we'll do the
> wrong thing eventually.
> 
> Anyway, gdb definitely does currently assume that symbols are in the
> host encoding.  E.g., the other caller of find_frame_funname calls
> fprintf_symbol_filtered, which may print the argument string directly.

Ok, I guess it would make sense to have symbol data structures to use
host encoding, and make the symbol readers convert as appropriate.

I committed the following, with the fix you mentioned. Thanks!
-- 
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center


gdb/
	Expose frames to Python.
	* Makefile.in (SUBDIR_PYTHON_OBS): Add python-frame.o.
	(SUBDIR_PYTHON_SRCS): Add python-frame.c.
	(python-frame.o): New target.
	* python/python-frame.c: New file.
	* python/python-internal.h (gdbpy_frames, gdbpy_newest_frame,
	gdbpy_frame_stop_reason_string, gdbpy_selected_frame,
	gdbpy_initialize_frames): New prototypes.
	* python/python.c (_initialize_python): Call gdbpy_initialize_frames.
	(GdbMethods): Add `selected_frame' and `frame_stop_reason_string'
	entries.
	* stack.c (find_frame_funname): New function, factored out of
	print_frame.
	(print_frame): Call find_frame_funname.
	* stack.h (find_frame_funname): Add prototype.

gdb/doc/
	* gdb.texinfo (Frames in Python): New node.
	(Python API): Update.

gdb/testsuite/
	* gdb.python/python-frame.c: New file.
	* gdb.python/python-frame.exp: New file.

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 6b69881..2b5e0ac 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -266,12 +266,14 @@ SUBDIR_TUI_CFLAGS= \
 SUBDIR_PYTHON_OBS = \
 	python.o \
 	python-cmd.o \
+	python-frame.o \
 	python-function.o \
 	python-utils.o \
 	python-value.o
 SUBDIR_PYTHON_SRCS = \
 	python/python.c \
 	python/python-cmd.c \
+	python/python-frame.c \
 	python/python-function.c \
 	python/python-utils.c \
 	python/python-value.c
@@ -1847,6 +1849,10 @@ python-cmd.o: $(srcdir)/python/python-cmd.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-cmd.c
 	$(POSTCOMPILE)
 
+python-frame.o: $(srcdir)/python/python-frame.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-frame.c
+	$(POSTCOMPILE)
+
 python-function.o: $(srcdir)/python/python-function.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-function.c
 	$(POSTCOMPILE)
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 166039b..431f3ae 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -18194,6 +18194,7 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
 * Values From Inferior::
 * Commands In Python::          Implementing new commands in Python.
 * Functions In Python::         Writing new convenience functions.
+* Frames In Python::            Acessing inferior stack frames from Python.
 @end menu
 
 @node Basic Python
@@ -18702,6 +18703,84 @@ registration of the function with @value{GDBN}.  Depending on how the
 Python code is read into @value{GDBN}, you may need to import the
 @code{gdb} module explicitly.
 
+@node Frames In Python
+@subsubsection Acessing inferior stack frames from Python.
+
+@cindex frames in python
+When the debugged program stops, @value{GDBN} is able to analyze its call
+stack (@pxref{Frames,,Stack frames}).  The @code{gdb.Frame} class
+represents a frame in the stack.  A @code{gdb.Frame} object is only valid
+while its corresponding frame exists in the inferior's stack.  If you try
+to use an invalid frame object, @value{GDBN} will throw a @code{RuntimeError}
+exception.
+
+Two @code{gdb.Frame} objects can be compared for equality with the @code{==}
+operator, like:
+
+@smallexample
+(@value{GDBP}) python print gdb.newest_frame() == gdb.selected_frame ()
+True
+@end smallexample
+
+The following frame-related functions are available in the @code{gdb} module:
+
+@findex gdb.selected_frame
+@defun selected_frame
+Return the selected frame object.  (@pxref{Selection,,Selecting a Frame}).
+@end defun
+
+@defun frame_stop_reason_string reason
+Return a string explaining the reason why @value{GDBN} stopped unwinding
+frames, as expressed by the given @var{reason} code (an integer, see the
+@code{unwind_stop_reason} method further down in this section).
+@end defun
+
+A @code{gdb.Frame} object has the following methods:
+
+@table @code
+@defmethod Frame is_valid
+Returns true if the @code{gdb.Frame} object is valid, false if not.
+A frame object can become invalid if the frame it refers to doesn't
+exist anymore in the inferior.  All @code{gdb.Frame} methods will throw
+an exception if it is invalid at the time the method is called.
+@end defmethod
+
+@defmethod Frame name
+Returns the function name of the frame, or @code{None} if it can't be
+obtained.
+@end defmethod
+
+@defmethod Frame type
+Returns the type of the frame.  The value can be one of
+@code{gdb.NORMAL_FRAME}, @code{gdb.DUMMY_FRAME}, @code{gdb.SIGTRAMP_FRAME}
+or @code{gdb.SENTINEL_FRAME}.
+@end defmethod
+
+@defmethod Frame unwind_stop_reason
+Return an integer representing the reason why it's not possible to find
+more frames toward the outermost frame.  Use
+@code{gdb.frame_stop_reason_string} to convert the value returned by this
+function to a string.
+@end defmethod
+
+@defmethod Frame pc
+Returns the frame's resume address.
+@end defmethod
+
+@defmethod Frame older
+Return the frame that called this frame.
+@end defmethod
+
+@defmethod Frame newer
+Return the frame called by this frame.
+@end defmethod
+
+@defmethod Frame read_var variable
+Return the value of the given variable in this frame.  @var{variable} must
+be a string.
+@end defmethod
+@end table
+
 @node Interpreters
 @chapter Command Interpreters
 @cindex command interpreters
diff --git a/gdb/python/python-frame.c b/gdb/python/python-frame.c
new file mode 100644
index 0000000..8d6127e
--- /dev/null
+++ b/gdb/python/python-frame.c
@@ -0,0 +1,538 @@
+/* Python interface to stack frames
+
+   Copyright (C) 2008, 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 "charset.h"
+#include "block.h"
+#include "frame.h"
+#include "exceptions.h"
+#include "symtab.h"
+#include "stack.h"
+#include "value.h"
+#include "python-internal.h"
+
+typedef struct {
+  PyObject_HEAD
+  struct frame_id frame_id;
+  struct gdbarch *gdbarch;
+
+  /* Marks that the FRAME_ID member actually holds the ID of the frame next
+     to this, and not this frames' ID itself.  This is a hack to permit Python
+     frame objects which represent invalid frames (i.e., the last frame_info
+     in a corrupt stack).  The problem arises from the fact that this code
+     relies on FRAME_ID to uniquely identify a frame, which is not always true
+     for the last "frame" in a corrupt stack (it can have a null ID, or the same
+     ID as the  previous frame).  Whenever get_prev_frame returns NULL, we
+     record the frame_id of the next frame and set FRAME_ID_IS_NEXT to 1.  */
+  int frame_id_is_next;
+} frame_object;
+
+/* Require a valid frame.  This must be called inside a TRY_CATCH, or
+   another context in which a gdb exception is allowed.  */
+#define FRAPY_REQUIRE_VALID(frame_obj, frame)		\
+    do {						\
+      frame = frame_object_to_frame_info (frame_obj);	\
+      if (frame == NULL)				\
+	error ("Frame is invalid.");			\
+    } while (0)
+
+static PyTypeObject frame_object_type;
+
+/* Returns the frame_info object corresponding to the given Python Frame
+   object.  If the frame doesn't exist anymore (the frame id doesn't
+   correspond to any frame in the inferior), returns NULL.  */
+
+static struct frame_info *
+frame_object_to_frame_info (frame_object *frame_obj)
+{
+  struct frame_info *frame;
+
+  frame = frame_find_by_id (frame_obj->frame_id);
+  if (frame == NULL)
+    return NULL;
+
+  if (frame_obj->frame_id_is_next)
+    frame = get_prev_frame (frame);
+
+  return frame;
+}
+
+/* Called by the Python interpreter to obtain string representation
+   of the object.  */
+
+static PyObject *
+frapy_str (PyObject *self)
+{
+  char *s;
+  long len;
+  PyObject *result;
+  struct ui_file *strfile;
+
+  strfile = mem_fileopen ();
+  fprint_frame_id (strfile, ((frame_object *) self)->frame_id);
+  s = ui_file_xstrdup (strfile, &len);
+  result = PyString_FromString (s);
+  xfree (s);
+
+  return result;
+}
+
+/* Implementation of gdb.Frame.is_valid (self) -> Boolean.
+   Returns True if the frame corresponding to the frame_id of this
+   object still exists in the inferior.  */
+
+static PyObject *
+frapy_is_valid (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame;
+
+  frame = frame_object_to_frame_info ((frame_object *) self);
+  if (frame == NULL)
+    Py_RETURN_FALSE;
+
+  Py_RETURN_TRUE;
+}
+
+/* Implementation of gdb.Frame.name (self) -> String.
+   Returns the name of the function corresponding to this frame.  */
+
+static PyObject *
+frapy_name (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame;
+  char *name;
+  enum language lang;
+  PyObject *result;
+  volatile struct gdb_exception except;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+      find_frame_funname (frame, &name, &lang);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  if (name)
+    result = PyUnicode_Decode (name, strlen (name), host_charset (), NULL);
+  else
+    {
+      result = Py_None;
+      Py_INCREF (Py_None);
+    }
+
+  return result;
+}
+
+/* Implementation of gdb.Frame.type (self) -> Integer.
+   Returns the frame type, namely one of the gdb.*_FRAME constants.  */
+
+static PyObject *
+frapy_type (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame;
+  enum frame_type type = NORMAL_FRAME;/* Initialize to appease gcc warning.  */
+  volatile struct gdb_exception except;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+      type = get_frame_type (frame);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  return PyInt_FromLong (type);
+}
+
+/* Implementation of gdb.Frame.unwind_stop_reason (self) -> Integer.
+   Returns one of the gdb.FRAME_UNWIND_* constants.  */
+
+static PyObject *
+frapy_unwind_stop_reason (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame = NULL;    /* Initialize to appease gcc warning.  */
+  volatile struct gdb_exception except;
+  enum unwind_stop_reason stop_reason;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  stop_reason = get_frame_unwind_stop_reason (frame);
+
+  return PyInt_FromLong (stop_reason);
+}
+
+/* Implementation of gdb.Frame.pc (self) -> Long.
+   Returns the frame's resume address.  */
+
+static PyObject *
+frapy_pc (PyObject *self, PyObject *args)
+{
+  CORE_ADDR pc = 0;	      /* Initialize to appease gcc warning.  */
+  struct frame_info *frame;
+  volatile struct gdb_exception except;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+      pc = get_frame_pc (frame);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  return PyLong_FromUnsignedLongLong (pc);
+}
+
+/* Convert a frame_info struct to a Python Frame object.
+   Sets a Python exception and returns NULL on error.  */
+
+static frame_object *
+frame_info_to_frame_object (struct frame_info *frame)
+{
+  frame_object *frame_obj;
+
+  frame_obj = PyObject_New (frame_object, &frame_object_type);
+  if (frame_obj == NULL)
+    {
+      PyErr_SetString (PyExc_MemoryError, "Could not allocate frame object.");
+      return NULL;
+    }
+
+  /* Try to get the previous frame, to determine if this is the last frame
+     in a corrupt stack.  If so, we need to store the frame_id of the next
+     frame and not of this one (which is possibly invalid).  */
+  if (get_prev_frame (frame) == NULL
+      && get_frame_unwind_stop_reason (frame) != UNWIND_NO_REASON
+      && get_next_frame (frame) != NULL)
+    {
+      frame_obj->frame_id = get_frame_id (get_next_frame (frame));
+      frame_obj->frame_id_is_next = 1;
+    }
+  else
+    {
+      frame_obj->frame_id = get_frame_id (frame);
+      frame_obj->frame_id_is_next = 0;
+    }
+
+  frame_obj->gdbarch = get_frame_arch (frame);
+
+  return frame_obj;
+}
+
+/* Implementation of gdb.Frame.older (self) -> gdb.Frame.
+   Returns the frame immediately older (outer) to this frame, or None if
+   there isn't one.  */
+
+static PyObject *
+frapy_older (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame, *prev;
+  volatile struct gdb_exception except;
+  PyObject *prev_obj = NULL;   /* Initialize to appease gcc warning.  */
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+      prev = get_prev_frame (frame);
+      if (prev)
+	prev_obj = (PyObject *) frame_info_to_frame_object (prev);
+      else
+	{
+	  Py_INCREF (Py_None);
+	  prev_obj = Py_None;
+	}
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  return prev_obj;
+}
+
+/* Implementation of gdb.Frame.newer (self) -> gdb.Frame.
+   Returns the frame immediately newer (inner) to this frame, or None if
+   there isn't one.  */
+
+static PyObject *
+frapy_newer (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame, *next;
+  volatile struct gdb_exception except;
+  PyObject *next_obj = NULL;   /* Initialize to appease gcc warning.  */
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+      next = get_next_frame (frame);
+      if (next)
+	next_obj = (PyObject *) frame_info_to_frame_object (next);
+      else
+	{
+	  Py_INCREF (Py_None);
+	  next_obj = Py_None;
+	}
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  return next_obj;
+}
+
+/* Implementation of gdb.Frame.read_var_value (self, variable) -> gdb.Value.
+   Returns the value of the given variable in this frame.  The argument must be
+   a string.  Returns None if GDB can't find the specified variable.  */
+
+static PyObject *
+frapy_read_var (PyObject *self, PyObject *args)
+{
+  struct frame_info *frame;
+  PyObject *sym_obj;
+  struct symbol *var = NULL;	/* gcc-4.3.2 false warning.  */
+  struct value *val = NULL;
+  volatile struct gdb_exception except;
+
+  if (!PyArg_ParseTuple (args, "O", &sym_obj))
+    return NULL;
+
+  if (gdbpy_is_string (sym_obj))
+    {
+      char *var_name;
+      struct block *block = NULL;
+      struct cleanup *cleanup;
+      volatile struct gdb_exception except;
+
+      var_name = python_string_to_target_string (sym_obj);
+      if (!var_name)
+	return NULL;
+      cleanup = make_cleanup (xfree, var_name);
+
+      TRY_CATCH (except, RETURN_MASK_ALL)
+	{
+	  FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+	  block = block_for_pc (get_frame_address_in_block (frame));
+	  var = lookup_symbol (var_name, block, VAR_DOMAIN, NULL);
+	}
+      GDB_PY_HANDLE_EXCEPTION (except);
+
+      if (!var)
+	{
+	  PyErr_Format (PyExc_ValueError,
+			_("variable '%s' not found"), var_name);
+	  do_cleanups (cleanup);
+
+	  return NULL;
+	}
+
+      do_cleanups (cleanup);
+    }
+  else
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       _("argument must be a symbol or string"));
+      return NULL;
+    }
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+
+      val = read_var_value (var, frame);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  if (val)
+    return value_to_value_object (val);
+
+  Py_RETURN_NONE;
+}
+
+/* Implementation of gdb.selected_frame () -> gdb.Frame.
+   Returns the selected frame object.  */
+
+PyObject *
+gdbpy_selected_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_selected_frame ("No frame is currently selected.");
+      frame_obj = frame_info_to_frame_object (frame);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  return (PyObject *) frame_obj;
+}
+
+/* Implementation of gdb.stop_reason_string (Integer) -> String.
+   Return a string explaining the unwind stop reason.  */
+
+PyObject *
+gdbpy_frame_stop_reason_string (PyObject *self, PyObject *args)
+{
+  int reason;
+  const char *str;
+
+  if (!PyArg_ParseTuple (args, "i", &reason))
+    return NULL;
+
+  if (reason < 0 || reason > UNWIND_NO_SAVED_PC)
+    {
+      PyErr_SetString (PyExc_ValueError, "Invalid frame stop reason.");
+      return NULL;
+    }
+
+  str = frame_stop_reason_string (reason);
+  return PyUnicode_Decode (str, strlen (str), host_charset (), NULL);
+}
+
+/* Implements the equality comparison for Frame objects.
+   All other comparison operators will throw a TypeError Python exception,
+   as they aren't valid for frames.  */
+
+static PyObject *
+frapy_richcompare (PyObject *self, PyObject *other, int op)
+{
+  if (!PyObject_TypeCheck (other, &frame_object_type))
+    {
+      PyErr_SetString (PyExc_TypeError, "Frame object expected in comparison.");
+      return NULL;
+    }
+  else if (op != Py_EQ)
+    {
+      PyErr_SetString (PyExc_TypeError, "Invalid comparison for gdb.Frame.");
+      return NULL;
+    }
+
+  if (frame_id_eq (((frame_object *) self)->frame_id,
+		   ((frame_object *) other)->frame_id))
+    Py_RETURN_TRUE;
+
+  Py_RETURN_FALSE;
+}
+
+/* Sets up the Frame API in the gdb module.  */
+
+void
+gdbpy_initialize_frames (void)
+{
+  if (PyType_Ready (&frame_object_type) < 0)
+    return;
+
+  /* Note: These would probably be best exposed as class attributes of Frame,
+     but I don't know how to do it except by messing with the type's dictionary.
+     That seems too messy.  */
+  PyModule_AddIntConstant (gdb_module, "NORMAL_FRAME", NORMAL_FRAME);
+  PyModule_AddIntConstant (gdb_module, "DUMMY_FRAME", DUMMY_FRAME);
+  PyModule_AddIntConstant (gdb_module, "SIGTRAMP_FRAME", SIGTRAMP_FRAME);
+  PyModule_AddIntConstant (gdb_module, "SENTINEL_FRAME", SENTINEL_FRAME);
+  PyModule_AddIntConstant (gdb_module,
+			   "FRAME_UNWIND_NO_REASON", UNWIND_NO_REASON);
+  PyModule_AddIntConstant (gdb_module,
+			   "FRAME_UNWIND_NULL_ID", UNWIND_NULL_ID);
+  PyModule_AddIntConstant (gdb_module,
+			   "FRAME_UNWIND_FIRST_ERROR", UNWIND_FIRST_ERROR);
+  PyModule_AddIntConstant (gdb_module,
+			   "FRAME_UNWIND_INNER_ID", UNWIND_INNER_ID);
+  PyModule_AddIntConstant (gdb_module,
+			   "FRAME_UNWIND_SAME_ID", UNWIND_SAME_ID);
+  PyModule_AddIntConstant (gdb_module,
+			   "FRAME_UNWIND_NO_SAVED_PC", UNWIND_NO_SAVED_PC);
+
+  Py_INCREF (&frame_object_type);
+  PyModule_AddObject (gdb_module, "Frame", (PyObject *) &frame_object_type);
+}
+
+\f
+
+static PyMethodDef frame_object_methods[] = {
+  { "is_valid", frapy_is_valid, METH_NOARGS,
+    "is_valid () -> Boolean.\n\
+Return true if this frame is valid, false if not." },
+  { "name", frapy_name, METH_NOARGS,
+    "name () -> String.\n\
+Return the function name of the frame, or None if it can't be determined." },
+  { "type", frapy_type, METH_NOARGS,
+    "type () -> Integer.\n\
+Return the type of the frame." },
+  { "unwind_stop_reason", frapy_unwind_stop_reason, METH_NOARGS,
+    "unwind_stop_reason () -> Integer.\n\
+Return the reason why it's not possible to find frames older than this." },
+  { "pc", frapy_pc, METH_NOARGS,
+    "pc () -> Long.\n\
+Return the frame's resume address." },
+  { "older", frapy_older, METH_NOARGS,
+    "older () -> gdb.Frame.\n\
+Return the frame that called this frame." },
+  { "newer", frapy_newer, METH_NOARGS,
+    "newer () -> gdb.Frame.\n\
+Return the frame called by this frame." },
+  { "read_var", frapy_read_var, METH_VARARGS,
+    "read_var (variable) -> gdb.Value.\n\
+Return the value of the variable in this frame." },
+  {NULL}  /* Sentinel */
+};
+
+static PyTypeObject frame_object_type = {
+  PyObject_HEAD_INIT (NULL)
+  0,				  /* ob_size */
+  "gdb.Frame",			  /* tp_name */
+  sizeof (frame_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 */
+  frapy_str,			  /* tp_str */
+  0,				  /* tp_getattro */
+  0,				  /* tp_setattro */
+  0,				  /* tp_as_buffer */
+  Py_TPFLAGS_DEFAULT,		  /* tp_flags */
+  "GDB frame object",		  /* tp_doc */
+  0,				  /* tp_traverse */
+  0,				  /* tp_clear */
+  frapy_richcompare,		  /* tp_richcompare */
+  0,				  /* tp_weaklistoffset */
+  0,				  /* tp_iter */
+  0,				  /* tp_iternext */
+  frame_object_methods,		  /* 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/python-internal.h b/gdb/python/python-internal.h
index 463f08e..f8d0896 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -64,12 +64,15 @@ extern PyObject *gdb_module;
 extern PyTypeObject value_object_type;
 
 PyObject *gdbpy_history (PyObject *self, PyObject *args);
+PyObject *gdbpy_frame_stop_reason_string (PyObject *, PyObject *);
+PyObject *gdbpy_selected_frame (PyObject *self, PyObject *args);
 
 PyObject *value_to_value_object (struct value *v);
 
 struct value *convert_value_from_python (PyObject *obj);
 
 void gdbpy_initialize_values (void);
+void gdbpy_initialize_frames (void);
 void gdbpy_initialize_commands (void);
 void gdbpy_initialize_functions (void);
 
diff --git a/gdb/python/python.c b/gdb/python/python.c
index b48cf05..52fc780 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -411,6 +411,7 @@ Enables or disables printing of Python stack traces."),
   PyModule_AddStringConstant (gdb_module, "TARGET_CONFIG", (char*) target_name);
 
   gdbpy_initialize_values ();
+  gdbpy_initialize_frames ();
   gdbpy_initialize_commands ();
   gdbpy_initialize_functions ();
 
@@ -464,6 +465,13 @@ static PyMethodDef GdbMethods[] =
   { "get_parameter", get_parameter, METH_VARARGS,
     "Return a gdb parameter's value" },
 
+  { "selected_frame", gdbpy_selected_frame, METH_NOARGS,
+    "selected_frame () -> gdb.Frame.\n\
+Return the selected frame object." },
+  { "frame_stop_reason_string", gdbpy_frame_stop_reason_string, METH_VARARGS,
+    "stop_reason_string (Integer) -> String.\n\
+Return a string explaining unwind stop reason." },
+
   { "write", gdbpy_write, METH_VARARGS,
     "Write a string using gdb's filtered stream." },
   { "flush", gdbpy_flush, METH_NOARGS,
diff --git a/gdb/stack.c b/gdb/stack.c
index bf9e576..7e31394 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -644,20 +644,16 @@ print_frame_info (struct frame_info *frame, int print_level,
   gdb_flush (gdb_stdout);
 }
 
-static void
-print_frame (struct frame_info *frame, int print_level,
-	     enum print_what print_what, int print_args,
-	     struct symtab_and_line sal)
+/* Attempt to obtain the FUNNAME and FUNLANG of the function corresponding
+   to FRAME.  */
+void
+find_frame_funname (struct frame_info *frame, char **funname,
+		    enum language *funlang)
 {
   struct symbol *func;
-  char *funname = NULL;
-  enum language funlang = language_unknown;
-  struct ui_stream *stb;
-  struct cleanup *old_chain, *list_chain;
-  struct value_print_options opts;
 
-  stb = ui_out_stream_new (uiout);
-  old_chain = make_cleanup_ui_out_stream_delete (stb);
+  *funname = NULL;
+  *funlang = language_unknown;
 
   func = find_pc_function (get_frame_address_in_block (frame));
   if (func)
@@ -690,24 +686,24 @@ print_frame (struct frame_info *frame, int print_level,
 	  /* We also don't know anything about the function besides
 	     its address and name.  */
 	  func = 0;
-	  funname = SYMBOL_PRINT_NAME (msymbol);
-	  funlang = SYMBOL_LANGUAGE (msymbol);
+	  *funname = SYMBOL_PRINT_NAME (msymbol);
+	  *funlang = SYMBOL_LANGUAGE (msymbol);
 	}
       else
 	{
-	  funname = SYMBOL_PRINT_NAME (func);
-	  funlang = SYMBOL_LANGUAGE (func);
-	  if (funlang == language_cplus)
+	  *funname = SYMBOL_PRINT_NAME (func);
+	  *funlang = SYMBOL_LANGUAGE (func);
+	  if (*funlang == language_cplus)
 	    {
 	      /* It seems appropriate to use SYMBOL_PRINT_NAME() here,
 		 to display the demangled name that we already have
 		 stored in the symbol table, but we stored a version
 		 with DMGL_PARAMS turned on, and here we don't want to
 		 display parameters.  So remove the parameters.  */
-	      char *func_only = cp_remove_params (funname);
+	      char *func_only = cp_remove_params (*funname);
 	      if (func_only)
 		{
-		  funname = func_only;
+		  *funname = func_only;
 		  make_cleanup (xfree, func_only);
 		}
 	    }
@@ -720,10 +716,27 @@ print_frame (struct frame_info *frame, int print_level,
 
       if (msymbol != NULL)
 	{
-	  funname = SYMBOL_PRINT_NAME (msymbol);
-	  funlang = SYMBOL_LANGUAGE (msymbol);
+	  *funname = SYMBOL_PRINT_NAME (msymbol);
+	  *funlang = SYMBOL_LANGUAGE (msymbol);
 	}
     }
+}
+
+static void
+print_frame (struct frame_info *frame, int print_level,
+	     enum print_what print_what, int print_args,
+	     struct symtab_and_line sal)
+{
+  char *funname = NULL;
+  enum language funlang = language_unknown;
+  struct ui_stream *stb;
+  struct cleanup *old_chain, *list_chain;
+  struct value_print_options opts;
+
+  stb = ui_out_stream_new (uiout);
+  old_chain = make_cleanup_ui_out_stream_delete (stb);
+
+  find_frame_funname (frame, &funname, &funlang);
 
   annotate_frame_begin (print_level ? frame_relative_level (frame) : 0,
 			get_frame_pc (frame));
@@ -759,7 +772,7 @@ print_frame (struct frame_info *frame, int print_level,
       struct print_args_args args;
       struct cleanup *args_list_chain;
       args.frame = frame;
-      args.func = func;
+      args.func = find_pc_function (get_frame_address_in_block (frame));
       args.stream = gdb_stdout;
       args_list_chain = make_cleanup_ui_out_list_begin_end (uiout, "args");
       catch_errors (print_args_stub, &args, "", RETURN_MASK_ERROR);
diff --git a/gdb/stack.h b/gdb/stack.h
index 973a57f..56b1d91 100644
--- a/gdb/stack.h
+++ b/gdb/stack.h
@@ -22,4 +22,9 @@
 
 void select_frame_command (char *level_exp, int from_tty);
 
+/* Attempt to obtain the FUNNAME and FUNLANG of the function corresponding
+   to FRAME.  */
+void find_frame_funname (struct frame_info *frame, char **funname,
+			 enum language *funlang);
+
 #endif /* #ifndef STACK_H */
diff --git a/gdb/testsuite/gdb.python/python-frame.c b/gdb/testsuite/gdb.python/python-frame.c
new file mode 100644
index 0000000..22eb9f2
--- /dev/null
+++ b/gdb/testsuite/gdb.python/python-frame.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-frame.exp b/gdb/testsuite/gdb.python/python-frame.exp
new file mode 100644
index 0000000..b1ee9be
--- /dev/null
+++ b/gdb/testsuite/gdb.python/python-frame.exp
@@ -0,0 +1,86 @@
+# 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 values to Python.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+set testfile "python-frame"
+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 f1 = gdb.selected_frame ()" "get second frame" 0
+gdb_py_test_silent_cmd "python f0 = f1.newer ()" "get first frame" 0
+
+gdb_test "python print 'result =', f0 == f1" " = False" "test equality comparison (false)"
+gdb_test "python print 'result =', f0 == f0" " = True" "test equality comparison (true)"
+gdb_test "python print 'result =', f0.is_valid ()" " = True" "test Frame.is_valid"
+gdb_test "python print 'result =', f0.name ()" " = f2" "test Frame.name"
+gdb_test "python print 'result =', f0.type () == gdb.NORMAL_FRAME" " = True" "test Frame.type"
+gdb_test "python print 'result =', f0.unwind_stop_reason () == gdb.FRAME_UNWIND_NO_REASON" " = True" "test Frame.type"
+gdb_test "python print 'result =', gdb.frame_stop_reason_string (gdb.FRAME_UNWIND_INNER_ID)" " = previous frame inner to this frame \\(corrupt stack\\?\\)" "test gdb.frame_stop_reason_string"
+gdb_test "python print 'result =', f0.pc ()" " = \[0-9\]+" "test Frame.pc"
+gdb_test "python print 'result =', f0.older () == f1" " = True" "test Frame.older"
+gdb_test "python print 'result =', f1.newer () == f0" " = True" "test Frame.newer"
+gdb_test "python print 'result =', f0.read_var ('variable_which_surely_doesnt_exist')" \
+  "ValueError: variable 'variable_which_surely_doesnt_exist' not found.*Error while executing Python code." \
+  "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.selected_frame () == f1" " = True" "test gdb.selected_frame"



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

end of thread, other threads:[~2009-03-30 19:57 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-03-10 18:00 [rfc] python API exposing inferior's frame stack Thiago Jung Bauermann
2009-03-10 23:37 ` Eli Zaretskii
2009-03-15 18:35   ` Thiago Jung Bauermann
2009-03-15 19:30     ` Eli Zaretskii
2009-03-16  3:36       ` Thiago Jung Bauermann
2009-03-16  4:07         ` Eli Zaretskii
2009-03-20 23:04           ` Thiago Jung Bauermann
2009-03-21  8:34             ` Eli Zaretskii
2009-03-30  5:33               ` Thiago Jung Bauermann
2009-03-30 18:35                 ` Eli Zaretskii
2009-03-30 19:09                 ` Tom Tromey
2009-03-30 19:24                   ` Thiago Jung Bauermann
2009-03-30 19:57                     ` Tom Tromey
2009-03-30 20:50                       ` Thiago Jung Bauermann
2009-03-22 16:00             ` Tom Tromey
2009-03-17 21:13   ` Tom Tromey
2009-03-17 22:43     ` Eli Zaretskii
2009-03-18  0:35       ` Tom Tromey
2009-03-18  4:12         ` Eli Zaretskii

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox