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]

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


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



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