From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 15082 invoked by alias); 3 Apr 2009 00:47:37 -0000 Received: (qmail 15064 invoked by uid 22791); 3 Apr 2009 00:47:34 -0000 X-SWARE-Spam-Status: No, hits=-0.4 required=5.0 tests=AWL,BAYES_50,J_CHICKENPOX_37,J_CHICKENPOX_38,SPF_HELO_PASS,SPF_PASS X-Spam-Check-By: sourceware.org Received: from mx2.redhat.com (HELO mx2.redhat.com) (66.187.237.31) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Fri, 03 Apr 2009 00:47:27 +0000 Received: from int-mx2.corp.redhat.com (int-mx2.corp.redhat.com [172.16.27.26]) by mx2.redhat.com (8.13.8/8.13.8) with ESMTP id n330lL2q003098; Thu, 2 Apr 2009 20:47:21 -0400 Received: from ns3.rdu.redhat.com (ns3.rdu.redhat.com [10.11.255.199]) by int-mx2.corp.redhat.com (8.13.1/8.13.1) with ESMTP id n330lLll029269; Thu, 2 Apr 2009 20:47:22 -0400 Received: from opsy.redhat.com (vpn-12-111.rdu.redhat.com [10.11.12.111]) by ns3.rdu.redhat.com (8.13.8/8.13.8) with ESMTP id n330lKpp023584; Thu, 2 Apr 2009 20:47:20 -0400 Received: by opsy.redhat.com (Postfix, from userid 500) id 42C0EC881B9; Thu, 2 Apr 2009 18:47:17 -0600 (MDT) To: Thiago Jung Bauermann Cc: gdb-patches@sourceware.org Subject: Re: Python pretty-printing [2/6] References: <1238711355.3236.51.camel@localhost.localdomain> From: Tom Tromey Reply-To: Tom Tromey Date: Fri, 03 Apr 2009 00:47:00 -0000 In-Reply-To: (Tom Tromey's message of "Thu\, 02 Apr 2009 18\:23\:29 -0600") Message-ID: User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.2 (gnu/linux) MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2009-04/txt/msg00055.txt.bz2 Here is the updated patch. Thank goodness for git rebase. Tom 2009-04-01 Tom Tromey Thiago Jung Bauermann Phil Muldoon * 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 Thiago Jung Bauermann Phil Muldoon * gdb.texinfo (Python API): Update. (Auto-loading): New node. (Objfiles In Python): New node. gdb/ChangeLog | 22 ++++ gdb/Makefile.in | 6 + gdb/doc/ChangeLog | 8 ++ gdb/doc/gdb.texinfo | 83 +++++++++++++++ gdb/python/python-internal.h | 4 + gdb/python/python-objfile.c | 229 ++++++++++++++++++++++++++++++++++++++++++ gdb/python/python.c | 146 +++++++++++++++++++++++++++ 7 files changed, 498 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..70f2a40 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 @@ -18291,6 +18293,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 +18744,49 @@ 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}. + +The following objfile-related functions are available in the +@code{gdb} module: + +@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 + +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..b70a006 --- /dev/null +++ b/gdb/python/python-objfile.c @@ -0,0 +1,229 @@ +/* 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 . */ + +#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) +{ + PyObject *tmp; + 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; + } + + /* Take care in case the LHS and RHS are related somehow. */ + tmp = self->printers; + Py_INCREF (value); + self->printers = value; + Py_XDECREF (tmp); + + 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 a borrowed reference to 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 @@ -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