This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


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

[rfc] python API exposing inferior's frame stack.


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);
+}
+
+
+
+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"



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