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]

Python pretty-printing [2/6]


This patch adds a minimal Python wrapper for struct objfile,
and arranges to auto-load Python code when an objfile is created.

This auto-loading makes the pretty-printing feature convenient for
users.  For example, we have a patch with a full suite of printers for
libstdc++.  libstdc++ will install these printers in a place visible
to gdb (see patch #1); gdb will automatically load them and so the
user will not have to do anything to see the benefits.

There was some disagreement about this feature on the Archer list.  In
particular, Daniel did not want to tie pretty-printing to objfiles.
However, I think this is still ok because this is just one method for
loading such code -- it need not be the only method.  That is, the
printers are separate from the method chosen to read them into gdb.

Tom

2009-04-01  Tom Tromey  <tromey@redhat.com>
	    Thiago Jung Bauermann  <bauerman@br.ibm.com>
	    Phil Muldoon  <pmuldoon@redhat.com>

	* python/python.c: Include objfiles.h, observer.h.
	(gdbpy_auto_load): New global.
	(gdbpy_current_objfile): Likewise.
	(GDBPY_AUTO_FILENAME): New define.
	(gdbpy_new_objfile): New function.
	(gdbpy_get_current_objfile): Likewise.
	(gdbpy_objfiles): Likewise.
	(_initialize_python): Add "maint set auto-load".  Call
	gdbpy_initialize_objfile.  Attach objfile observer.
	(GdbMethods): New methods current_objfile, objfiles.
	* python/python-objfile.c: New file.
	* python/python-internal.h (objfile_to_objfile_object): Declare.
	(objfpy_get_printers): Likewise.
	(gdbpy_initialize_objfile): Likewise.
	* Makefile.in (SUBDIR_PYTHON_OBS): Add python-objfile.o.
	(SUBDIR_PYTHON_SRCS): Add python-objfile.c.
	(python-objfile.o): New target.

2009-04-01  Tom Tromey  <tromey@redhat.com>
	    Thiago Jung Bauermann  <bauerman@br.ibm.com>
	    Phil Muldoon  <pmuldoon@redhat.com>

	* gdb.texinfo (Python API): Update.
	(Basic Python): Document current_objfile, objfiles.
	(Auto-loading): New node.
	(Objfiles In Python): New node.

 gdb/ChangeLog                |   22 ++++
 gdb/Makefile.in              |    6 +
 gdb/doc/ChangeLog            |    9 ++
 gdb/doc/gdb.texinfo          |   80 +++++++++++++++
 gdb/python/python-internal.h |    4 +
 gdb/python/python-objfile.c  |  225 ++++++++++++++++++++++++++++++++++++++++++
 gdb/python/python.c          |  146 +++++++++++++++++++++++++++
 7 files changed, 492 insertions(+), 0 deletions(-)
 create mode 100644 gdb/python/python-objfile.c

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 6b69881..dbd2126 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -267,12 +267,14 @@ SUBDIR_PYTHON_OBS = \
 	python.o \
 	python-cmd.o \
 	python-function.o \
+	python-objfile.o \
 	python-utils.o \
 	python-value.o
 SUBDIR_PYTHON_SRCS = \
 	python/python.c \
 	python/python-cmd.c \
 	python/python-function.c \
+	python/python-objfile.c \
 	python/python-utils.c \
 	python/python-value.c
 SUBDIR_PYTHON_DEPS =
@@ -1851,6 +1853,10 @@ python-function.o: $(srcdir)/python/python-function.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-function.c
 	$(POSTCOMPILE)
 
+python-objfile.o: $(srcdir)/python/python-objfile.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-objfile.c
+	$(POSTCOMPILE)
+
 python-utils.o: $(srcdir)/python/python-utils.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-utils.c
 	$(POSTCOMPILE)
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 0dff6e0..bcfc9bf 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -18191,9 +18191,11 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
 @menu
 * Basic Python::                Basic Python Functions.
 * Exception Handling::
+* Auto-loading::                Automatically loading Python code.
 * Values From Inferior::
 * Commands In Python::          Implementing new commands in Python.
 * Functions In Python::         Writing new convenience functions.
+* Objfiles In Python::          Object files.
 @end menu
 
 @node Basic Python
@@ -18219,6 +18221,20 @@ command as having originated from the user invoking it interactively.
 It must be a boolean value.  If omitted, it defaults to @code{False}.
 @end defun
 
+@findex gdb.current_objfile
+@defun current_objfile
+When auto-loading a Python script (@pxref{Auto-loading}), @value{GDBN}
+sets the ``current objfile'' to the corresponding objfile.  This
+function returns the current objfile.  If there is no current objfile,
+this function returns @code{None}.
+@end defun
+
+@findex gdb.objfiles
+@defun objfiles
+Return a sequence of all the objfiles current known to @value{GDBN}.
+@xref{Objfiles In Python}.
+@end defun
+
 @findex gdb.get_parameter
 @defun get_parameter parameter
 Return the value of a @value{GDBN} parameter.  @var{parameter} is a
@@ -18291,6 +18307,44 @@ message as its value, and the Python call stack backtrace at the
 Python statement closest to where the @value{GDBN} error occured as the
 traceback.
 
+@node Auto-loading
+@subsubsection Auto-loading
+
+When a new object file (@pxref{Objfiles In Python}) is read (for
+example, due to the @code{file} command, or because the inferior has
+loaded a shared library), @value{GDBN} will look for a file named by
+adding @samp{-gdb.py} to the object file's real name (the name formed
+after following all symlinks and resolving @code{.} and @code{..}
+components).  If this file exists and is readable, @value{GDBN} will
+evaluate it as a Python script.
+
+If this file does not exist, and if the parameter
+@code{debug-file-directory} is set, then @value{GDBN} will append the
+object file's real name to the value of this parameter, and try again.
+
+Finally, if this file does not exist, then @value{GDBN} will look in
+subdirectory of @code{gdb_datadir} (whose value is available from
+@code{maint show gdb_datadir}).  Specifically, @value{GDBN} will take
+the value of @code{gdb_datadir}, append @samp{python/auto-load}, and
+then append the object file's real name.
+
+Also, if a separate debug file is used, @value{GDBN} will look for the
+@samp{-gdb.py} file both in the directory associated with the
+application and the directory associated with the separate debug file.
+
+When reading a @samp{-gdb.py} file, @value{GDBN} sets the ``current
+objfile''.  This is available via the @code{gdb.current_objfile}
+function.  This can be useful for registering objfile-specific
+pretty-printers.
+
+This feature is useful for supplying application-specific debugging
+commands and scripts.  It can be disabled using @code{maint set python
+auto-load}.
+
+@value{GDBN} does not track which files it has already auto-loaded.
+So, your @samp{-gdb.py} file should take care to ensure that it may be
+evaluated multiple times without error.
+
 @node Values From Inferior
 @subsubsection Values From Inferior
 @cindex values from inferior, with Python
@@ -18704,6 +18758,32 @@ 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 Objfiles In Python
+@subsubsection Objfiles In Python
+
+@cindex objfiles in python
+@cindex python objfiles
+@tindex gdb.Objfile
+@tindex Objfile
+@value{GDBN} loads symbols for an inferior from various
+symbol-containing files.  These include the primary executable file,
+any shared libraries used by the inferior, and any separate debug info
+files.  @value{GDBN} calls these symbol-containing files
+@dfn{objfiles}.
+
+Each objfile is represented by an instance of the @code{gdb.Objfile}
+class.
+
+@defivar Objfile filename
+The file name of the objfile as a string.
+@end defivar
+
+@defivar Objfile pretty_printers
+The @code{pretty_printers} attribute is used to look up
+pretty-printers by type.  This is a dictionary which maps regular
+expressions (strings) to pretty-printing objects.
+@end defivar
+
 @node Interpreters
 @chapter Command Interpreters
 @cindex command interpreters
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 463f08e..9764f4f 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -66,12 +66,16 @@ extern PyTypeObject value_object_type;
 PyObject *gdbpy_history (PyObject *self, PyObject *args);
 
 PyObject *value_to_value_object (struct value *v);
+PyObject *objfile_to_objfile_object (struct objfile *);
+
+PyObject *objfpy_get_printers (PyObject *, void *);
 
 struct value *convert_value_from_python (PyObject *obj);
 
 void gdbpy_initialize_values (void);
 void gdbpy_initialize_commands (void);
 void gdbpy_initialize_functions (void);
+void gdbpy_initialize_objfile (void);
 
 struct cleanup *make_cleanup_py_decref (PyObject *py);
 struct cleanup *make_cleanup_py_restore_gil (PyGILState_STATE *state);
diff --git a/gdb/python/python-objfile.c b/gdb/python/python-objfile.c
new file mode 100644
index 0000000..f5db084
--- /dev/null
+++ b/gdb/python/python-objfile.c
@@ -0,0 +1,225 @@
+/* Python interface to objfiles.
+
+   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 "python-internal.h"
+#include "charset.h"
+#include "objfiles.h"
+
+typedef struct
+{
+  PyObject_HEAD
+
+  /* The corresponding objfile.  */
+  struct objfile *objfile;
+
+  /* The pretty-printer list of functions.  */
+  PyObject *printers;
+} objfile_object;
+
+static PyTypeObject objfile_object_type;
+
+static const struct objfile_data *objfpy_objfile_data_key;
+
+
+
+/* An Objfile method which returns the objfile's file name, or None.  */
+static PyObject *
+objfpy_get_filename (PyObject *self, void *closure)
+{
+  objfile_object *obj = (objfile_object *) self;
+  if (obj->objfile && obj->objfile->name)
+    return PyString_Decode (obj->objfile->name, strlen (obj->objfile->name),
+			    host_charset (), NULL);
+  Py_RETURN_NONE;
+}
+
+static void
+objfpy_dealloc (PyObject *o)
+{
+  objfile_object *self = (objfile_object *) o;
+  Py_XDECREF (self->printers);
+  self->ob_type->tp_free ((PyObject *) self);
+}
+
+static PyObject *
+objfpy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
+{
+  objfile_object *self = (objfile_object *) type->tp_alloc (type, 0);
+  if (self)
+    {
+      self->objfile = NULL;
+
+      self->printers = PyList_New (0);
+      if (!self->printers)
+	{
+	  Py_DECREF (self);
+	  return NULL;
+	}
+    }
+  return (PyObject *) self;
+}
+
+PyObject *
+objfpy_get_printers (PyObject *o, void *ignore)
+{
+  objfile_object *self = (objfile_object *) o;
+  Py_INCREF (self->printers);
+  return self->printers;
+}
+
+static int
+objfpy_set_printers (PyObject *o, PyObject *value, void *ignore)
+{
+  objfile_object *self = (objfile_object *) o;
+  if (! value)
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       "cannot delete the pretty_printers attribute");
+      return -1;
+    }
+
+  if (! PyList_Check (value))
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       "the pretty_printers attribute must be a list");
+      return -1;
+    }
+
+  Py_XDECREF (self->printers);
+  Py_INCREF (value);
+  self->printers = value;
+
+  return 0;
+}
+
+
+
+/* Clear the OBJFILE pointer in an Objfile object and remove the
+   reference.  */
+static void
+clean_up_objfile (struct objfile *objfile, void *datum)
+{
+  PyGILState_STATE state;
+  objfile_object *object = datum;
+
+  state = PyGILState_Ensure ();
+  object->objfile = NULL;
+  Py_DECREF ((PyObject *) object);
+  PyGILState_Release (state);
+}
+
+/* Return the Python object of type Objfile representing OBJFILE.  If
+   the object has already been created, return it.  Otherwise, create
+   it.  Return NULL and set the Python error on failure.  */
+PyObject *
+objfile_to_objfile_object (struct objfile *objfile)
+{
+  objfile_object *object;
+
+  object = objfile_data (objfile, objfpy_objfile_data_key);
+  if (!object)
+    {
+      object = PyObject_New (objfile_object, &objfile_object_type);
+      if (object)
+	{
+	  PyObject *dict;
+
+	  object->objfile = objfile;
+
+	  object->printers = PyList_New (0);
+	  if (!object->printers)
+	    {
+	      Py_DECREF (object);
+	      return NULL;
+	    }
+
+	  set_objfile_data (objfile, objfpy_objfile_data_key, object);
+	}
+    }
+
+  return (PyObject *) object;
+}
+
+void
+gdbpy_initialize_objfile (void)
+{
+  objfpy_objfile_data_key
+    = register_objfile_data_with_cleanup (clean_up_objfile);
+
+  if (PyType_Ready (&objfile_object_type) < 0)
+    return;
+
+  Py_INCREF (&objfile_object_type);
+  PyModule_AddObject (gdb_module, "Objfile", (PyObject *) &objfile_object_type);
+}
+
+
+
+static PyGetSetDef objfile_getset[] =
+{
+  { "filename", objfpy_get_filename, NULL,
+    "The objfile's filename, or None.", NULL },
+  { "pretty_printers", objfpy_get_printers, objfpy_set_printers,
+    "Pretty printers.", NULL },
+  { NULL }
+};
+
+static PyTypeObject objfile_object_type =
+{
+  PyObject_HEAD_INIT (NULL)
+  0,				  /*ob_size*/
+  "gdb.Objfile",		  /*tp_name*/
+  sizeof (objfile_object),	  /*tp_basicsize*/
+  0,				  /*tp_itemsize*/
+  objfpy_dealloc,		  /*tp_dealloc*/
+  0,				  /*tp_print*/
+  0,				  /*tp_getattr*/
+  0,				  /*tp_setattr*/
+  0,				  /*tp_compare*/
+  0,				  /*tp_repr*/
+  0,				  /*tp_as_number*/
+  0,				  /*tp_as_sequence*/
+  0,				  /*tp_as_mapping*/
+  0,				  /*tp_hash */
+  0,				  /*tp_call*/
+  0,				  /*tp_str*/
+  0,				  /*tp_getattro*/
+  0,				  /*tp_setattro*/
+  0,				  /*tp_as_buffer*/
+  Py_TPFLAGS_DEFAULT,		  /*tp_flags*/
+  "GDB objfile object",		  /* tp_doc */
+  0,				  /* tp_traverse */
+  0,				  /* tp_clear */
+  0,				  /* tp_richcompare */
+  0,				  /* tp_weaklistoffset */
+  0,				  /* tp_iter */
+  0,				  /* tp_iternext */
+  0,				  /* tp_methods */
+  0,				  /* tp_members */
+  objfile_getset,		  /* tp_getset */
+  0,				  /* tp_base */
+  0,				  /* tp_dict */
+  0,				  /* tp_descr_get */
+  0,				  /* tp_descr_set */
+  0,				  /* tp_dictoffset */
+  0,				  /* tp_init */
+  0,				  /* tp_alloc */
+  objfpy_new,			  /* tp_new */
+};
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 1762c46..5044b8a 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -22,6 +22,8 @@
 #include "ui-out.h"
 #include "cli/cli-script.h"
 #include "gdbcmd.h"
+#include "objfiles.h"
+#include "observer.h"
 
 #include <ctype.h>
 
@@ -29,6 +31,10 @@
    false otherwise.  */
 static int gdbpy_should_print_stack = 1;
 
+/* This is true if we should auto-load python code when an objfile is
+   opened, false otherwise.  */
+static int gdbpy_auto_load = 1;
+
 #ifdef HAVE_PYTHON
 
 #include "python.h"
@@ -301,6 +307,129 @@ gdbpy_print_stack (void)
     PyErr_Clear ();
 }
 
+
+
+/* The "current" objfile.  This is set when gdb detects that a new
+   objfile has been loaded.  It is only set for the duration of a call
+   to gdbpy_new_objfile; it is NULL at other times.  */
+static struct objfile *gdbpy_current_objfile;
+
+/* The file name we attempt to read.  */
+#define GDBPY_AUTO_FILENAME "-gdb.py"
+
+/* This is a new_objfile observer callback which loads python code
+   based on the path to the objfile.  */
+static void
+gdbpy_new_objfile (struct objfile *objfile)
+{
+  char *realname;
+  char *filename, *debugfile;
+  int len;
+  FILE *input;
+  PyGILState_STATE state;
+  struct cleanup *cleanups;
+
+  if (!gdbpy_auto_load || !objfile || !objfile->name)
+    return;
+
+  state = PyGILState_Ensure ();
+
+  gdbpy_current_objfile = objfile;
+
+  realname = gdb_realpath (objfile->name);
+  len = strlen (realname);
+  filename = xmalloc (len + sizeof (GDBPY_AUTO_FILENAME));
+  memcpy (filename, realname, len);
+  strcpy (filename + len, GDBPY_AUTO_FILENAME);
+
+  input = fopen (filename, "r");
+  debugfile = filename;
+
+  cleanups = make_cleanup (xfree, filename);
+  make_cleanup (xfree, realname);
+
+  if (!input && debug_file_directory)
+    {
+      /* Also try the same file in the separate debug info directory.  */
+      debugfile = xmalloc (strlen (filename)
+			   + strlen (debug_file_directory) + 1);
+      strcpy (debugfile, debug_file_directory);
+      /* FILENAME is absolute, so we don't need a "/" here.  */
+      strcat (debugfile, filename);
+
+      make_cleanup (xfree, debugfile);
+      input = fopen (debugfile, "r");
+    }
+
+  if (!input && gdb_datadir)
+    {
+      /* Also try the same file in a subdirectory of gdb's data
+	 directory.  */
+      debugfile = xmalloc (strlen (gdb_datadir) + strlen (filename)
+			   + strlen ("/auto-load") + 1);
+      strcpy (debugfile, gdb_datadir);
+      strcat (debugfile, "/auto-load");
+      /* FILENAME is absolute, so we don't need a "/" here.  */
+      strcat (debugfile, filename);
+
+      make_cleanup (xfree, debugfile);
+      input = fopen (debugfile, "r");
+    }
+
+  if (input)
+    {
+      /* We don't want to throw an exception here -- but the user
+	 would like to know that something went wrong.  */
+      if (PyRun_SimpleFile (input, debugfile))
+	gdbpy_print_stack ();
+      fclose (input);
+    }
+
+  do_cleanups (cleanups);
+  gdbpy_current_objfile = NULL;
+
+  PyGILState_Release (state);
+}
+
+/* Return the current Objfile, or None if there isn't one.  */
+static PyObject *
+gdbpy_get_current_objfile (PyObject *unused1, PyObject *unused2)
+{
+  PyObject *result;
+
+  if (! gdbpy_current_objfile)
+    Py_RETURN_NONE;
+
+  result = objfile_to_objfile_object (gdbpy_current_objfile);
+  if (result)
+    Py_INCREF (result);
+  return result;
+}
+
+/* Return a sequence holding all the Objfiles.  */
+static PyObject *
+gdbpy_objfiles (PyObject *unused1, PyObject *unused2)
+{
+  struct objfile *objf;
+  PyObject *list;
+
+  list = PyList_New (0);
+  if (!list)
+    return NULL;
+
+  ALL_OBJFILES (objf)
+  {
+    PyObject *item = objfile_to_objfile_object (objf);
+    if (!item || PyList_Append (list, item) == -1)
+      {
+	Py_DECREF (list);
+	return NULL;
+      }
+  }
+
+  return list;
+}
+
 #else /* HAVE_PYTHON */
 
 /* Dummy implementation of the gdb "python" command.  */
@@ -399,6 +528,15 @@ Enables or disables printing of Python stack traces."),
 			   &set_python_list,
 			   &show_python_list);
 
+  add_setshow_boolean_cmd ("auto-load", class_maintenance,
+			   &gdbpy_auto_load, _("\
+Enable or disable auto-loading of Python code when an object is opened."), _("\
+Show whether Python code will be auto-loaded when an object is opened."), _("\
+Enables or disables auto-loading of Python code when an object is opened."),
+			   NULL, NULL,
+			   &set_python_list,
+			   &show_python_list);
+
 #ifdef HAVE_PYTHON
   Py_Initialize ();
   PyEval_InitThreads ();
@@ -419,9 +557,12 @@ Enables or disables printing of Python stack traces."),
   gdbpy_initialize_values ();
   gdbpy_initialize_commands ();
   gdbpy_initialize_functions ();
+  gdbpy_initialize_objfile ();
 
   PyRun_SimpleString ("import gdb");
 
+  observer_attach_new_objfile (gdbpy_new_objfile);
+
   gdbpy_doc_cst = PyString_FromString ("__doc__");
 
   /* Create a couple objects which are used for Python's stdout and
@@ -479,6 +620,11 @@ static PyMethodDef GdbMethods[] =
   { "get_parameter", get_parameter, METH_VARARGS,
     "Return a gdb parameter's value" },
 
+  { "current_objfile", gdbpy_get_current_objfile, METH_NOARGS,
+    "Return the current Objfile being loaded, or None." },
+  { "objfiles", gdbpy_objfiles, METH_NOARGS,
+    "Return a sequence of all loaded objfiles." },
+
   { "write", gdbpy_write, METH_VARARGS,
     "Write a string using gdb's filtered stream." },
   { "flush", gdbpy_flush, METH_NOARGS,
-- 
1.6.0.6


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