* [patch] PR python/10807 API for macros.
@ 2011-08-16 6:36 Matt Rice
2011-08-16 10:02 ` Phil Muldoon
0 siblings, 1 reply; 5+ messages in thread
From: Matt Rice @ 2011-08-16 6:36 UTC (permalink / raw)
To: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 1652 bytes --]
In the gdb.Macro class, I didn't used methods instead of attributes,
because the values are lazily computed/can return different objects if
called multiple times, thus attributes seemed weird, even though I
think the Macro methods are fairly attribute like. so if anyone has
any opinion here on attributes vs methods.
contains one unrelated change, to stpy_dealloc which seemed to leak.
its missing macro expansion, user defined macros, a sal/scoped version
of the macros() method
and whatever else anyone can think of, will save that for another time.
2011-08-15 Matt Rice <ratmice@gmail.com>
PR python/10807
* NEWS: Mention new macro api.
* Makefile.in: Add py-macro.c.
* python/py-macro.c: New file.
* python/py-objfile.c (objfpy_symtab): New function.
(objfile_getset): Add symtab attribute.
* python/py-symtab.c (add_macro_to_list, stpy_macros,
salpy_macro_named): New functions.
(stpy_dealloc): Free the PyObject.
(symtab_object_methods): Add macros method.
(sal_object_methods): Add macro_named method.
* python/py-symtab.h: New File:
(symtab_to_symtab_object): Make public.
* python/python.h: Add gdb_initialize_macros.
* python/python.c: Call gdb_initialize_macros.
2011-08-15 Matt Rice <ratmice@gmail.com>
* gdb.python/py-macro.c: New file.
* gdb.python/py-macro.exp: Ditto.
2011-08-15 Matt Rice <ratmice@gmail.com>
* gdb.texinfo (Macros In Python): New section.
(Symbols In Python): Add macro_named, and macros methods.
(Objfiles In Python): Add symtab attribute.
[-- Attachment #2: foo.diff --]
[-- Type: text/x-patch, Size: 32429 bytes --]
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index bd00644..71389a6 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -289,6 +289,7 @@ SUBDIR_PYTHON_OBS = \
py-inferior.o \
py-infthread.o \
py-lazy-string.o \
+ py-macro.o \
py-objfile.o \
py-param.o \
py-prettyprint.o \
@@ -319,6 +320,7 @@ SUBDIR_PYTHON_SRCS = \
python/py-inferior.c \
python/py-infthread.c \
python/py-lazy-string.c \
+ python/py-macro.c \
python/py-objfile.c \
python/py-param.c \
python/py-prettyprint.c \
@@ -2110,6 +2112,10 @@ py-lazy-string.o: $(srcdir)/python/py-lazy-string.c
$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-lazy-string.c
$(POSTCOMPILE)
+py-macro.o: $(srcdir)/python/py-macro.c
+ $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-macro.c
+ $(POSTCOMPILE)
+
py-objfile.o: $(srcdir)/python/py-objfile.c
$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-objfile.c
$(POSTCOMPILE)
diff --git a/gdb/NEWS b/gdb/NEWS
index 089e6ce..a6c2d11 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -30,6 +30,13 @@
** Symbols now provide the "type" attribute, the type of the symbol.
+ ** Objfiles now provide a "symtab" attribute.
+
+ ** The Macro object is now available for representing preprocessor macros.
+ The following objects now have methods for macro lookup.
+ - Symtab_and_line now has a "macro_named" method
+ - Symtab now has a "macros" method.
+
* libthread-db-search-path now supports two special values: $sdir and $pdir.
$sdir specifies the default system locations of shared libraries.
$pdir specifies the directory where the libpthread used by the application
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index b477cf3..db74e67 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -20955,6 +20955,7 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
* Blocks In Python:: Accessing frame blocks from Python.
* Symbols In Python:: Python representation of symbols.
* Symbol Tables In Python:: Python representation of symbol tables.
+* Macros In Python:: Python representation of preprocessor macros.
* Lazy Strings In Python:: Python representation of lazy strings.
* Breakpoints In Python:: Manipulating breakpoints using Python.
@end menu
@@ -22945,6 +22946,10 @@ which is used to format the value. @xref{Pretty Printing API}, for more
information.
@end defivar
+@defivar Objfile symtab
+The objfile's symbol table represented as a @code{gdb.Symtab} object.
+@end defivar
+
A @code{gdb.Objfile} object has the following methods:
@defmethod Objfile is_valid
@@ -23415,6 +23420,11 @@ exist in @value{GDBN} any longer. All other
@code{gdb.Symtab_and_line} methods will throw an exception if it is
invalid at the time the method is called.
@end defmethod
+
+@defmethod Symtab_and_line macro_named @r{[}name@r{]}
+Returns a @code{gdb.Macro} object, for the macro
+with the given name.
+@end defmethod
@end table
A @code{gdb.Symtab} object has the following attributes:
@@ -23444,8 +23454,48 @@ if it is invalid at the time the method is called.
@defmethod Symtab fullname
Return the symbol table's source absolute file name.
@end defmethod
+
+@defmethod Symtab macros
+Return all of the macros contained in the symbol table.
+@end defmethod
@end table
+
+@node Macros In Python
+@subsubsection Python representation of preprocessor macros.
+
+@cindex macros in python
+@tindex gdb.macro
+
+Python code can query information about preprocessor macros using the
+@code{gdb.macro} class. For obtaining a @code{gdb.macro} object see
+@xref{Symbol Tables In Python}.
+
+@defmethod Macro name
+Returns the name of the macro.
+@end defmethod
+
+@defmethod Macro definition
+Returns a string with the definition of the macro.
+@end defmethod
+
+@defmethod Macro is_function_like
+Returns @code{true} If the macro is function like.
+@end defmethod
+
+@defmethod Macro argv
+Returns a list of the macros arguments if the macro is function like.
+If the macro is not function like, returns @code{None}
+@end defmethod
+
+@defmethod Macro include_trail
+Returns a list of tuples containing the filenames, and line numbers
+of header and source files that correspond to the include directives
+and file location that caused the macro to be defined.
+The list is sorted starting with the place of definition,
+and ending with the first include directive.
+@end defmethod
+
@node Breakpoints In Python
@subsubsection Manipulating breakpoints using Python
diff --git a/gdb/python/py-macro.c b/gdb/python/py-macro.c
new file mode 100644
index 0000000..4b2f385
--- /dev/null
+++ b/gdb/python/py-macro.c
@@ -0,0 +1,463 @@
+/* Python interface to macros.
+
+ Copyright (C) 2011 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 "macrotab.h"
+#include "macroexp.h"
+#include "macroscope.h"
+#include "charset.h"
+
+typedef struct
+{
+ PyObject_HEAD;
+ const char *name;
+ struct macro_source_file *src_file;
+ int src_line;
+} macro_object;
+
+static PyTypeObject macro_object_type;
+
+static PyObject *
+definition_to_py (struct macro_definition *macro)
+{
+ if (macro->replacement)
+ return PyString_FromString (macro->replacement);
+ else
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+is_function_like_to_py (struct macro_definition *macro)
+{
+ if (macro->kind == macro_function_like)
+ Py_RETURN_TRUE;
+ else
+ Py_RETURN_FALSE;
+}
+
+static PyObject *
+argv_to_py (struct macro_definition *macro)
+{
+ PyObject *ret = NULL;
+
+ if (macro->kind == macro_function_like)
+ {
+ Py_ssize_t i;
+ PyObject *ret = PyList_New (macro->argc);
+
+ if (ret == NULL)
+ return NULL;
+
+ for (i = 0; i < macro->argc; i++)
+ {
+ PyObject *item = PyString_FromString (macro->argv[i]);
+
+ if (!item)
+ goto err_ret;
+
+ if (PyList_SetItem (ret, i, item) != 0)
+ goto err_ret;
+ }
+
+ return ret;
+ }
+
+ Py_RETURN_NONE;
+
+ err_ret:
+ Py_XDECREF (ret);
+ return NULL;
+}
+
+
+static PyObject *
+include_trail_to_py(struct macro_definition *macro,
+ const char *name,
+ struct macro_source_file *src_file,
+ int src_line)
+{
+ PyObject *tuple = PyTuple_New (2);
+ PyObject *result = PyList_New (0);
+ PyObject *tmp;
+ struct macro_source_file *file;
+ int line;
+
+ if (!tuple || !result)
+ goto err_exit;
+
+ file = macro_definition_location (src_file, src_line, name, &line);
+ if (!file)
+ goto err_exit;
+
+ tmp = PyString_FromString (file->filename);
+ if (!tmp)
+ goto err_exit;
+ PyTuple_SetItem (tuple, 0, tmp);
+
+ tmp = PyInt_FromLong (line);
+ if (!tmp)
+ goto err_exit;
+ if (PyTuple_SetItem (tuple, 1, tmp) != 0)
+ goto err_exit;
+ if (PyList_Append (result, tuple) != 0)
+ goto err_exit;
+ Py_DECREF (tuple);
+
+ while (file->included_by)
+ {
+ tuple = PyTuple_New (2);
+
+ if (!tuple)
+ goto err_exit;
+
+ tmp = PyString_FromString (file->included_by->filename);
+ if (!tmp)
+ goto err_exit;
+ if (PyTuple_SetItem (tuple, 0, tmp) != 0)
+ goto err_exit;
+
+ tmp = PyInt_FromLong (file->included_at_line);
+ if (!tmp)
+ goto err_exit;
+ if (PyTuple_SetItem (tuple, 1, tmp) != 0)
+ goto err_exit;
+
+ if (PyList_Append (result, tuple) != 0)
+ goto err_exit;
+ Py_DECREF (tuple);
+
+ file = file->included_by;
+ }
+
+ return result;
+
+ err_exit:
+ Py_XDECREF (tuple);
+ Py_DECREF (result);
+ return NULL;
+}
+
+/* Create a new macro (gdb.Macro) object that encapsulates the
+ macro_definition structure from GDB. */
+PyObject *
+macropy_object_create (struct macro_definition *macro,
+ const char *name,
+ struct macro_source_file *ms,
+ int line)
+{
+ macro_object *macro_obj;
+
+ macro_obj = PyObject_New (macro_object, ¯o_object_type);
+ if (macro_obj)
+ {
+ /* Save enough to lookup the macro again in the methods.
+ Then do a lookup and lazily copy things into PyObjects.
+ Consecutive lookups should be OK, because of the splay tree.
+ We cannot save a macro definition due to inconsistent memory
+ management. We rely on the fact that the macro_source_file
+ is not released until exit.
+
+ It seems we may need to move to using a 'macro_scope'
+ if we want a python api for user-defined macros. */
+ macro_obj->src_file = ms;
+ macro_obj->src_line = line;
+ macro_obj->name = xstrdup (name);
+ }
+
+ return (PyObject *) macro_obj;
+}
+
+static void
+macropy_dealloc (PyObject *obj)
+{
+ macro_object *macro_obj = (macro_object *) obj;
+
+ xfree ((void *) macro_obj->name);
+ obj->ob_type->tp_free (obj);
+}
+
+static PyObject *
+macropy_name_method (PyObject *self, PyObject *args)
+{
+ macro_object *me = (macro_object *) self;
+
+ return PyString_Decode (me->name, strlen (me->name), host_charset (), NULL);
+}
+
+static PyObject *
+macropy_definition_method (PyObject *self, PyObject *args)
+{
+ macro_object *me = (macro_object *) self;
+ struct macro_definition *macro;
+
+ macro = macro_lookup_definition (me->src_file, me->src_line, me->name);
+ return definition_to_py (macro);
+}
+
+static PyObject *
+macropy_is_function_like_method (PyObject *self, PyObject *args)
+{
+ macro_object *me = (macro_object *) self;
+ struct macro_definition *macro;
+
+ macro = macro_lookup_definition (me->src_file, me->src_line, me->name);
+ return is_function_like_to_py (macro);
+}
+
+static PyObject *
+macropy_argv_method (PyObject *self, PyObject *args)
+{
+ macro_object *me = (macro_object *) self;
+ struct macro_definition *macro;
+
+ macro = macro_lookup_definition (me->src_file, me->src_line, me->name);
+ return argv_to_py (macro);
+}
+
+static PyObject *
+macropy_include_trail_method (PyObject *self, PyObject *args)
+{
+ macro_object *me = (macro_object *) self;
+ struct macro_definition *macro;
+
+ macro = macro_lookup_definition (me->src_file, me->src_line, me->name);
+ return include_trail_to_py (macro, me->name, me->src_file, me->src_line);
+}
+
+static int
+concat_chars (PyObject **result, const char *stuff)
+{
+ PyObject *tmp = PyString_FromString (stuff);
+
+ if (!tmp)
+ return -1;
+
+ PyString_ConcatAndDel (result, tmp);
+ if (*result == NULL)
+ return -1;
+
+ return 0;
+}
+
+static int
+concat_py_obj (PyObject **result, PyObject *stuff)
+{
+ PyObject *tmp = PyObject_Str (stuff);
+
+ if (!tmp)
+ return -1;
+
+ PyString_ConcatAndDel (result, tmp);
+ if (*result == NULL)
+ return -1;
+
+ return 0;
+}
+
+static PyObject *
+macropy_str (PyObject *self)
+{
+ PyObject *result = PyString_FromString ("<gdb.Macro ");
+ macro_object *me = (macro_object *) self;
+ struct macro_definition *macro;
+ PyObject *argv = macropy_argv_method (self, Py_None);
+ PyObject *definition = macropy_definition_method (self, Py_None);
+ PyObject *include_trail = macropy_include_trail_method (self, Py_None);
+ PyObject *is_function_like = macropy_is_function_like_method (self, Py_None);
+ PyObject *name = PyString_FromString (me->name);
+
+ if (!definition || !is_function_like || !argv
+ || !include_trail || !name || !result)
+ goto err_ret;
+
+ if (concat_py_obj (&result, name) != 0)
+ goto err_ret;
+
+ if (is_function_like == Py_True)
+ {
+ Py_ssize_t sz = PyList_Size (argv);
+ Py_ssize_t i;
+
+ if (concat_chars (&result, "(") != 0)
+ goto err_ret;
+
+ for (i = 0; i < sz; i++)
+ {
+ /* borrowed */
+ if (concat_py_obj (&result, PyList_GetItem (argv, i)) != 0)
+ goto err_ret;
+
+ if (i < sz - 1)
+ if (concat_chars (&result, ", ") != 0)
+ goto err_ret;
+ }
+
+ if (concat_chars (&result, ")") != 0)
+ goto err_ret;
+ }
+
+ if (definition != Py_None && PyString_Size (definition))
+ {
+ if (concat_chars (&result, "=") != 0)
+ goto err_ret;
+
+ if (concat_py_obj (&result, definition) != 0)
+ goto err_ret;
+ }
+
+ if (concat_chars (&result, " ") != 0)
+ goto err_ret;
+ if (concat_chars (&result, "include_trail=") != 0)
+ goto err_ret;
+ if (concat_py_obj (&result, include_trail) != 0)
+ goto err_ret;
+
+ if (concat_chars (&result, ">") != 0)
+ goto err_ret;
+
+ goto normal_ret;
+
+ err_ret:
+ Py_XDECREF (result);
+ result = NULL;
+ /* fall-through */
+ normal_ret:
+ Py_XDECREF (argv);
+ Py_XDECREF (definition);
+ Py_XDECREF (include_trail);
+ Py_XDECREF (is_function_like);
+ Py_XDECREF (name);
+ return result;
+}
+
+static int
+macropy_compare (PyObject *self, PyObject *o2)
+{
+ PyObject *my_str = macropy_str (self);
+ int result;
+
+ if (!my_str)
+ return -1;
+
+ if (PyObject_TypeCheck (o2, ¯o_object_type))
+ {
+ PyObject *other_str = macropy_str (o2);
+
+ if (other_str)
+ result = PyObject_Compare (my_str, other_str);
+ else
+ result = -1;
+
+ Py_DECREF (my_str);
+ Py_XDECREF (other_str);
+ return result;
+ }
+
+ result = PyObject_Compare (my_str, o2);
+
+ Py_DECREF (my_str);
+ return result;
+}
+
+static long
+macropy_hash(PyObject *o)
+{
+ long result;
+ PyObject *my_str = macropy_str (o);
+
+ if (!my_str)
+ return -1;
+
+ result = PyObject_Hash (my_str);
+ Py_DECREF (my_str);
+ return result;
+}
+
+void
+gdbpy_initialize_macros (void)
+{
+ macro_object_type.tp_new = PyType_GenericNew;
+
+ if (PyType_Ready (¯o_object_type) < 0)
+ return;
+
+ Py_INCREF (¯o_object_type);
+ PyModule_AddObject (gdb_module, "Macro",
+ (PyObject *) ¯o_object_type);
+}
+
+static PyGetSetDef macro_object_getset[] = {
+ {NULL} /* Sentinel */
+};
+
+static PyMethodDef macro_object_methods[] = {
+ { "argv", macropy_argv_method, METH_NOARGS,
+ "argv () -> List.\n\
+Return a list containing the names of the arguments for a function like \
+macro." },
+ { "definition", macropy_definition_method, METH_NOARGS,
+ "definition () -> String.\n\
+Return the macro's definition." },
+ { "include_trail", macropy_include_trail_method, METH_NOARGS,
+ "include_trail () -> List.\n\
+Return a list containing tuples with the filename and line number describing \
+how and where this macro came to be defined." },
+ { "is_function_like", macropy_is_function_like_method, METH_NOARGS,
+ "is_function_like () -> Bool.\n\
+Return True if the macro is function like, False otherwise." },
+ { "name", macropy_name_method, METH_NOARGS,
+ "name () -> String.\n\
+Return the macro's name." },
+ {NULL} /* Sentinel */
+};
+
+static PyTypeObject macro_object_type = {
+ PyObject_HEAD_INIT (NULL)
+ 0, /*ob_size*/
+ "gdb.Macro", /*tp_name*/
+ sizeof (macro_object), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ macropy_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ macropy_compare, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ macropy_hash, /*tp_hash */
+ 0, /*tp_call*/
+ macropy_str, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ "GDB macro object", /*tp_doc */
+ 0, /*tp_traverse */
+ 0, /*tp_clear */
+ 0, /*tp_richcompare */
+ 0, /*tp_weaklistoffset */
+ 0, /*tp_iter */
+ 0, /*tp_iternext */
+ macro_object_methods, /*tp_methods */
+ 0, /*tp_members */
+ macro_object_getset, /*tp_getset */
+};
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index f9821f5..2697218 100644
--- a/gdb/python/py-objfile.c
+++ b/gdb/python/py-objfile.c
@@ -22,6 +22,7 @@
#include "charset.h"
#include "objfiles.h"
#include "language.h"
+#include "py-symtab.h"
typedef struct
{
@@ -118,6 +119,17 @@ objfpy_set_printers (PyObject *o, PyObject *value, void *ignore)
return 0;
}
+static PyObject *
+objfpy_symtab (PyObject *self, void *ignore)
+{
+ objfile_object *obj = (objfile_object *) self;
+
+ if (! obj->objfile)
+ return Py_None;
+
+ return symtab_to_symtab_object (obj->objfile->symtabs);
+}
+
/* Implementation of gdb.Objfile.is_valid (self) -> Boolean.
Returns True if this object file still exists in GDB. */
@@ -210,6 +222,8 @@ static PyGetSetDef objfile_getset[] =
"The objfile's filename, or None.", NULL },
{ "pretty_printers", objfpy_get_printers, objfpy_set_printers,
"Pretty printers.", NULL },
+ { "symtab", objfpy_symtab, NULL,
+ "The objfile's symtab, or None.", NULL },
{ NULL }
};
diff --git a/gdb/python/py-symtab.c b/gdb/python/py-symtab.c
index 107cdec..033a21d 100644
--- a/gdb/python/py-symtab.c
+++ b/gdb/python/py-symtab.c
@@ -22,6 +22,7 @@
#include "symtab.h"
#include "source.h"
#include "python-internal.h"
+#include "py-macro.h"
#include "objfiles.h"
typedef struct stpy_symtab_object {
@@ -138,6 +139,66 @@ stpy_fullname (PyObject *self, PyObject *args)
Py_RETURN_NONE;
}
+struct macro_user_data {
+ PyObject *list;
+ int errors;
+};
+
+static void add_macro_to_list (const char *name,
+ const struct macro_definition *definition,
+ struct macro_source_file *source,
+ int line,
+ void *user_data)
+{
+ struct macro_user_data *mud = user_data;
+ PyObject *tmp;
+
+ if (! PyObject_TypeCheck (mud->list, &PyList_Type))
+ goto err_exit;
+
+ if (mud->errors != 0)
+ goto err_exit;
+
+ tmp = macropy_object_create (definition, name, source, line);
+ if (tmp)
+ {
+ if (PyList_Append (mud->list, tmp) != 0)
+ goto err_exit;
+ Py_DECREF (tmp);
+ return;
+ }
+
+ err_exit:
+ mud->errors = 1;
+ return;
+}
+
+static PyObject *
+stpy_macros (PyObject *self, PyObject *args)
+{
+ struct symtab *st = symtab_object_to_symtab (self);
+ struct macro_user_data mud;
+ PyObject *result;
+
+ STPY_REQUIRE_VALID (self, st);
+
+ result = PyList_New (0);
+ if (result == NULL)
+ return NULL;
+
+ mud.list = result;
+ mud.errors = 0;
+ macro_for_each (st->macro_table, add_macro_to_list, &mud);
+
+ if (mud.errors != 0)
+ {
+ Py_DECREF (result);
+ return NULL;
+ }
+
+ return result;
+}
+
/* Implementation of gdb.Symtab.is_valid (self) -> Boolean.
Returns True if this Symbol table still exists in GDB. */
@@ -191,6 +252,7 @@ stpy_dealloc (PyObject *obj)
if (symtab->next)
symtab->next->prev = symtab->prev;
symtab->symtab = NULL;
+ obj->ob_type->tp_free (obj);
}
@@ -242,6 +304,39 @@ salpy_is_valid (PyObject *self, PyObject *args)
Py_RETURN_TRUE;
}
+static PyObject *
+salpy_macro_named (PyObject *self, PyObject *args)
+{
+ struct symtab_and_line *sal;
+ struct macro_scope *ms;
+ struct macro_definition *mdef;
+ char *macro_name;
+ struct cleanup *cleanup_chain;
+ PyObject *result;
+
+ if (!PyArg_ParseTuple (args, "s", ¯o_name))
+ return NULL;
+
+ SALPY_REQUIRE_VALID (self, sal);
+
+ ms = sal_macro_scope (*sal);
+ cleanup_chain = make_cleanup (free_current_contents, &ms);
+ if (ms == NULL)
+ goto none_exit;
+
+ mdef = macro_lookup_definition (ms->file, ms->line, macro_name);
+ if (mdef == NULL)
+ goto none_exit;
+
+ result = macropy_object_create (mdef, macro_name, ms->file, ms->line);
+ do_cleanups (cleanup_chain);
+ return result;
+
+ none_exit:
+ do_cleanups (cleanup_chain);
+ return Py_None;
+}
+
static void
salpy_dealloc (PyObject *self)
{
@@ -477,6 +572,9 @@ Return true if this symbol table is valid, false if not." },
{ "fullname", stpy_fullname, METH_NOARGS,
"fullname () -> String.\n\
Return the symtab's full source filename." },
+ { "macros", stpy_macros, METH_NOARGS,
+ "macros () -> List.\n\
+Return a list of macros in the symtab." },
{NULL} /* Sentinel */
};
@@ -526,6 +624,10 @@ static PyMethodDef sal_object_methods[] = {
{ "is_valid", salpy_is_valid, METH_NOARGS,
"is_valid () -> Boolean.\n\
Return true if this symbol table and line is valid, false if not." },
+ { "macro_named", salpy_macro_named, METH_VARARGS,
+ "macro_named (name) -> Macro.\n\
+Return the macro object for the given name, \
+or None if the macro cannot be found." },
{NULL} /* Sentinel */
};
diff --git a/gdb/python/py-symtab.h b/gdb/python/py-symtab.h
new file mode 100644
index 0000000..911a051
--- /dev/null
+++ b/gdb/python/py-symtab.h
@@ -0,0 +1,7 @@
+#ifndef GDB_PY_SYMTAB_H
+#define GDB_PY_SYMTAB_H
+
+PyObject *
+symtab_to_symtab_object (struct symtab *symtab);
+
+#endif
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 996b23b..532552a 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -211,6 +211,7 @@ void gdbpy_initialize_breakpoint_event (void);
void gdbpy_initialize_continue_event (void);
void gdbpy_initialize_exited_event (void);
void gdbpy_initialize_thread_event (void);
+void gdbpy_initialize_macros (void);
struct cleanup *make_cleanup_py_decref (PyObject *py);
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 03edce9..092b354 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1198,6 +1198,7 @@ Enables or disables printing of Python stack traces."),
gdbpy_initialize_lazy_string ();
gdbpy_initialize_thread ();
gdbpy_initialize_inferior ();
+ gdbpy_initialize_macros ();
gdbpy_initialize_events ();
gdbpy_initialize_eventregistry ();
diff --git a/gdb/testsuite/gdb.python/py-macro.c b/gdb/testsuite/gdb.python/py-macro.c
new file mode 100644
index 0000000..8ac10e0
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-macro.c
@@ -0,0 +1,72 @@
+#ifdef DEF_MACROS
+
+ #ifdef ONE
+ #ifdef FOO
+ #undef FOO
+ #endif
+
+ #define FOO "hello"
+ #else
+ #undef FOO
+ #endif
+
+
+ #ifdef TWO
+ #ifdef FOO
+ #undef FOO
+ #endif
+ #define FOO " "
+ #endif
+
+ #ifdef THREE
+ #ifdef FOO
+ #undef FOO
+ #endif
+ #define FOO(a,b)
+ #endif
+
+ #ifdef FOUR
+ #ifdef FOO
+ #undef FOO
+ #endif
+ #define FOO(a) foo = a
+ #endif
+#else
+
+int main (int argc, const char **argv)
+{
+ char *foo;
+
+ #define DEF_MACROS
+ #define ONE
+ #include "py-macro.c"
+ foo = FOO;
+
+ #define TWO
+ #include "py-macro.c"
+ foo = FOO;
+
+ #define THREE
+ #include "py-macro.c"
+ foo = "world"FOO(0,1);
+
+ #undef THREE
+ #include "py-macro.c"
+ foo = FOO;
+
+ #undef TWO
+ #include "py-macro.c"
+ foo = FOO;
+
+ #undef ONE
+ #include "py-macro.c"
+ foo = (char *)0;
+
+ #define FOUR
+ #include "py-macro.c"
+ FOO ("the end.");
+
+ return 0;
+}
+#endif
+
diff --git a/gdb/testsuite/gdb.python/py-macro.exp b/gdb/testsuite/gdb.python/py-macro.exp
new file mode 100644
index 0000000..ccbabb6
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-macro.exp
@@ -0,0 +1,288 @@
+# Copyright (C) 2011 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 macros to Python.
+
+if $tracelevel then {
+ strace $tracelevel
+}
+
+load_lib gdb-python.exp
+
+set testfile "py-macro"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+get_compiler_info ${binfile}
+if [test_compiler_info gcc*] {
+ lappend options additional_flags=-g3
+} else {
+ untested ${testfile}.exp
+ return -1
+}
+
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile} $options] } {
+ untested ${testfile}.exp
+ return -1
+}
+
+# Start with a fresh gdb.
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+gdb_py_test_multiple "setup macro test objects" \
+ "python" "" \
+ "def find_objfile():" "" \
+ " for objfile in gdb.objfiles():" "" \
+ " if objfile.filename == \"$binfile\":" "" \
+ " print objfile.filename" "" \
+ " return objfile" "" \
+ "def here_macro(name):" "" \
+ " return gdb.decode_line(\"*\$pc\")\[1\]\[0\].macro_named(name)" "" \
+ "macros = list()" "" \
+ "end" ""
+
+gdb_test "python objfile = find_objfile()" "$binfile" "found objfile"
+gdb_test "python objfile.symtab.macros()" \
+ "Symbol Table is invalid.*" "symtab is invalid"
+
+if ![runto_main] {
+ untested ${testfile}.exp
+ return -1
+}
+
+gdb_py_test_silent_cmd "python m = objfile.symtab.macros()" "get all macros" 1
+
+gdb_py_test_silent_cmd "python include_guard = here_macro(\"DEF_MACROS\")" \
+ "get guard macro #1" \
+ 1
+
+gdb_test "python print include_guard.name() == \"DEF_MACROS\"" \
+ "True" \
+ "guard name"
+
+gdb_test "python print include_guard.is_function_like() == False" \
+ "True" \
+ "guard isnt function like"
+
+gdb_test "python print include_guard.argv() == None" \
+ "True" \
+ "guard has no args"
+
+gdb_test "python print 'foo' + include_guard.definition() + 'bar'" \
+ "foobar" \
+ "guard definition is empty"
+
+gdb_test "python print include_guard.include_trail()\[0\]\[0\]" \
+ "$srcfile.*" \
+ "guard trail has srcfile"
+
+#<gdb.Macro include_trail=[('./gdb.python/py-macro.c', 8), ('./gdb.python/py-macro.c', 43)] FOO="hello">
+gdb_test "python print include_guard" \
+"<gdb\.Macro DEF_MACROS include_trail=\\\[\\\('$srcdir/$subdir/$srcfile', \[0-9\]+\\\).*\\\]>" \
+"guard string rep"
+
+gdb_py_test_silent_cmd "python macros.append(include_guard)" \
+ "add to macros list" \
+ 0
+
+gdb_py_test_silent_cmd "python foo = here_macro(\"FOO\")" \
+ "get FOO macro #1" \
+ 1
+
+gdb_test "python print foo.name()" \
+ "FOO" \
+ "FOO macro #1 name"
+
+gdb_test "python print foo.is_function_like()" \
+ "False" \
+ "FOO macro #1 isnt function like"
+
+gdb_test "python print foo.argv()" \
+ "None" \
+ "FOO macro #1 has no args"
+
+gdb_test "python print foo.definition()" \
+ "\"hello\"" \
+ "FOO macro #1 definition is \"hello\""
+
+gdb_test "python print include_guard.include_trail()\[0\]\[0\]" \
+ "$srcfile.*" \
+ "FOO macro #1 has srcfile"
+
+gdb_test "python print foo" \
+"<gdb\.Macro FOO=\"hello\" include_trail=\\\[\\\('$srcdir/$subdir/$srcfile', \[0-9\]+\\\).*\\\]>" \
+"FOO macro #1 string rep"
+
+gdb_py_test_silent_cmd "python macros.append(foo)" \
+ "add to macros list" \
+ 0
+
+gdb_test "next" "" ""
+gdb_py_test_silent_cmd "python foo = here_macro(\"FOO\")" \
+ "get FOO macro #2" \
+ 1
+
+gdb_test "python print foo.name()" \
+ "FOO" \
+ "FOO macro #2 name"
+
+gdb_test "python print foo.is_function_like()" \
+ "False" \
+ "FOO macro #2 isnt function like"
+
+gdb_test "python print foo.argv()" \
+ "None" \
+ "FOO macro #2 has no args"
+
+gdb_test "python print foo.definition()" \
+ "\" \"" \
+ "FOO macro #2 definition is \" \""
+
+gdb_test "python print foo.include_trail()\[0\]\[0\]" \
+ "$srcfile.*" \
+ "FOO macro #2 has srcfile"
+
+gdb_test "python print foo" \
+"<gdb\.Macro FOO=\" \" include_trail=\\\[\\\('$srcdir/$subdir/$srcfile', \[0-9\]+\\\).*\\\]>" \
+"FOO macro #2 string rep"
+
+gdb_py_test_silent_cmd "python macros.append(foo)" \
+ "add to macros list" \
+ 0
+
+gdb_test "next" "" ""
+gdb_py_test_silent_cmd "python foo = here_macro(\"FOO\")" \
+ "get FOO macro #3" \
+ 1
+gdb_test "python print foo.name()" \
+ "FOO" \
+ "FOO macro #3 name"
+
+gdb_test "python print foo.is_function_like()" \
+ "True" \
+ "FOO macro #3 is function like"
+
+gdb_test "python print foo.argv()" \
+ "\\\['a', 'b'\\\]" \
+ "FOO macro #3 has args"
+
+gdb_test "python print 'foo' + foo.definition() + 'bar'" \
+ "foobar" \
+ "FOO macro #3 definition is empty"
+
+gdb_test "python print include_guard.include_trail()\[0\]\[0\]" \
+ "$srcfile.*" \
+ "FOO macro #3 has srcfile"
+
+gdb_test "python print foo" \
+"<gdb\.Macro FOO\\\(a, b\\\) include_trail=\\\[\\\('$srcdir/$subdir/$srcfile', \[0-9\]+\\\).*\\\]>" \
+"FOO macro #3 string rep"
+
+gdb_py_test_silent_cmd "python macros.append(foo)" \
+ "add to macros list" \
+ 0
+gdb_test "next" "" ""
+gdb_test "next" "" ""
+gdb_test "next" "" ""
+gdb_py_test_silent_cmd "python foo = here_macro(\"FOO\")" \
+ "get FOO macro #4" \
+ 1
+gdb_test "python print foo" \
+ "None" \
+ "there is no Foo macro #4"
+
+gdb_test "next" "" ""
+gdb_py_test_silent_cmd "python foo = here_macro(\"FOO\")" \
+ "get FOO macro #5" \
+ 1
+
+gdb_test "python print foo.name()" \
+ "" \
+ "FOO macro #5 name"
+
+gdb_test "python print foo.is_function_like()" \
+ "True" \
+ "FOO macro #5 is function like"
+
+gdb_test "python print foo.argv()" \
+ "\\\['a'\\\]" \
+ "FOO macro #5 has args"
+
+gdb_test "python print foo.definition()" \
+ "foo = a" \
+ "FOO macro #5 definition ok"
+
+gdb_test "python print include_guard.include_trail()\[0\]\[0\]" \
+ "$srcfile.*" \
+ "FOO macro #5 has srcfile"
+
+gdb_test "python print foo" \
+"<gdb\.Macro FOO\\\(a\\\)=foo = a include_trail=\\\[\\\('$srcdir/$subdir/$srcfile', \[0-9\]+\\\).*\\\]>" \
+"FOO macro #5 string rep"
+gdb_py_test_silent_cmd "python macros.append(foo)" \
+ "add to macros list" \
+ 0
+
+# this could find some ref count bugs if they were to happen for a singleton.
+gdb_py_test_multiple "run macros a couple of times" \
+ "python" "" \
+ "c = 0" "" \
+ "while c > 3:" "" \
+ " c = c + 1" "" \
+ " for macro in objfile.symtab.macros():" "" \
+ " macro.name()" "" \
+ " macro.is_function_like()" "" \
+ " macro.argv()" "" \
+ " macro.include_path()" "" \
+ " macro.definition()" "" \
+ " str(macro)" "" \
+ " hash(macro)" "" \
+ "end" ""
+
+gdb_py_test_multiple "find selected macros in the big list of macros" \
+ "python" "" \
+ "set1 = set(objfile.symtab.macros())" "" \
+ "set2 = set(macros)" "" \
+ "intersect = (set1 & set2)" "" \
+ "set3 = set(filter(lambda(x): x.name() == \"FOO\", objfile.symtab.macros()))" "" \
+ "intersect2 = (set3 & set2)" "" \
+ "print \"set2\", map(lambda(x): x.name(), set2)" "" \
+ "print \"set3\", map(lambda(x): x.name(), set3)" "" \
+ "print \"intersect\", map(lambda(x): x.name(), intersect)" "" \
+ "print \"intersect2\", map(lambda(x): x.name(), intersect2)" "" \
+ "print \"set2 - intersect\", map(lambda(x): x.name(), set2 - intersect)" "" \
+ "print \"intersect - set2\", map(lambda(x): x.name(), intersect - set2)" "" \
+ "print \"set2 - intersect2\", map(lambda(x): x.name(), set2 - intersect2)" "" \
+ "print \"intersect2 - set2\", map(lambda(x): x.name(), intersect2 - set2)" "" \
+ "end" ""
+
+gdb_test "python print len(set2)" "5" "macro set length 5"
+gdb_test "python print len(set1) > len(set2)" "True" "all macros is longer."
+gdb_test "python print set1 >= set2" "True" "macro set2 is a subset"
+gdb_test "python print len(intersect - set2), len(set2 - intersect)" \
+ "0 0" \
+ "macro set intersection equality"
+gdb_test "python print len(intersect2 - set2)" \
+ "0" \
+ "macro set intersection equality 2"
+gdb_test "python print len(set2 - intersect2), (set2 - intersect2).pop()" \
+ "1 <gdb.Macro DEF_MACROS.*>" \
+ "macro set intersection 3"
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [patch] PR python/10807 API for macros.
2011-08-16 6:36 [patch] PR python/10807 API for macros Matt Rice
@ 2011-08-16 10:02 ` Phil Muldoon
2011-08-16 15:16 ` Matt Rice
0 siblings, 1 reply; 5+ messages in thread
From: Phil Muldoon @ 2011-08-16 10:02 UTC (permalink / raw)
To: Matt Rice; +Cc: gdb-patches
Matt Rice <ratmice@gmail.com> writes:
> In the gdb.Macro class, I didn't used methods instead of attributes,
> because the values are lazily computed/can return different objects if
> called multiple times, thus attributes seemed weird, even though I
> think the Macro methods are fairly attribute like. so if anyone has
> any opinion here on attributes vs methods.
Thanks.
> +typedef struct
> +{
> + PyObject_HEAD;
> + const char *name;
> + struct macro_source_file *src_file;
> + int src_line;
> +} macro_object;
> +
> +static PyTypeObject macro_object_type;
We've been trying to put comments in typedef/struct definitions to
narrate each field. Some of the older Python files do not have them,
but the newer ones do. Just a one line comment on each field is fine.
> +static PyObject *
> +argv_to_py (struct macro_definition *macro)
> +{
> + PyObject *ret = NULL;
> +
> + if (macro->kind == macro_function_like)
> + {
> + Py_ssize_t i;
> + PyObject *ret = PyList_New (macro->argc);
> +
> + if (ret == NULL)
> + return NULL;
> +
> + for (i = 0; i < macro->argc; i++)
> + {
> + PyObject *item = PyString_FromString (macro->argv[i]);
> +
> + if (!item)
> + goto err_ret;
> +
> + if (PyList_SetItem (ret, i, item) != 0)
> + goto err_ret;
> + }
> +
> + return ret;
> + }
> +
> + Py_RETURN_NONE;
> +
> + err_ret:
> + Py_XDECREF (ret);
> + return NULL;
> +}
Some minor nits.
Some indents looks a little weird, but it might just be the mailer. You
don't need Py_XDECREF here as you already accounted for the PyList being
NULL. So just Py_DECREF.
> +
> +static PyObject *
> +include_trail_to_py(struct macro_definition *macro,
> + const char *name,
> + struct macro_source_file *src_file,
> + int src_line)
> +{
> + PyObject *tuple = PyTuple_New (2);
> + PyObject *result = PyList_New (0);
> + PyObject *tmp;
> + struct macro_source_file *file;
> + int line;
> +
> + if (!tuple || !result)
> + goto err_exit;
Even though either one of these indicates a failure case, and cannot
continue, you need to check the individual return status right after
instantiation. This is so you return the correct exception back to the
user.
> + file = macro_definition_location (src_file, src_line, name, &line);
> + if (!file)
> + goto err_exit;
I'm not sure it is right to do this. macro_definition_location is a GDB
function that can return NULL, not out of error, but because there is no
definition of a splay tree node for that definition. Looking at the
functions you call, and macro_definition_location calls, this can
expected and does not signal a Python error condition. While, at this
point it is right to exit because there is nothing more you can do,
returning NULL from a Python function raises the expectation there is a
Python error with an accompanying exception. If I remember correctly,
Python will complain about this. Perhaps it would be better in this
case to return None, and document the return strategy in the comments at
the function header.
> +
> + tmp = PyString_FromString (file->filename);
> + if (!tmp)
> + goto err_exit;
> + PyTuple_SetItem (tuple, 0, tmp);
> +
> + tmp = PyInt_FromLong (line);
> + if (!tmp)
> + goto err_exit;
> + if (PyTuple_SetItem (tuple, 1, tmp) != 0)
> + goto err_exit;
> + if (PyList_Append (result, tuple) != 0)
> + goto err_exit;
> + Py_DECREF (tuple);
> + while (file->included_by)
> + {
> + tuple = PyTuple_New (2);
> +
> + if (!tuple)
> + goto err_exit;
> +
> + tmp = PyString_FromString (file->included_by->filename);
> + if (!tmp)
> + goto err_exit;
> + if (PyTuple_SetItem (tuple, 0, tmp) != 0)
> + goto err_exit;
> +
> + tmp = PyInt_FromLong (file->included_at_line);
> + if (!tmp)
> + goto err_exit;
> + if (PyTuple_SetItem (tuple, 1, tmp) != 0)
> + goto err_exit;
> +
> + if (PyList_Append (result, tuple) != 0)
> + goto err_exit;
> + Py_DECREF (tuple);
> +
> + file = file->included_by;
> + }
> +
> + return result;
> +
> + err_exit:
> + Py_XDECREF (tuple);
> + Py_DECREF (result);
Either of these could be NULL, so you need to use XDECREF in both cases.
> + return NULL;
> +}
> +
> +/* Create a new macro (gdb.Macro) object that encapsulates the
> + macro_definition structure from GDB. */
> +PyObject *
> +macropy_object_create (struct macro_definition *macro,
> + const char *name,
> + struct macro_source_file *ms,
> + int line)
I normally try to document parameter usage here. And other publicly
facing functions, too.
+{
> + macro_object *macro_obj;
> +
> + macro_obj = PyObject_New (macro_object, ¯o_object_type);
> + if (macro_obj)
> + {
> + /* Save enough to lookup the macro again in the methods.
> + Then do a lookup and lazily copy things into PyObjects.
> + Consecutive lookups should be OK, because of the splay tree.
> + We cannot save a macro definition due to inconsistent memory
> + management. We rely on the fact that the macro_source_file
> + is not released until exit.
> +
> + It seems we may need to move to using a 'macro_scope'
> + if we want a python api for user-defined macros. */
What do you mean by inconsistent memory management? Can you expand/explain
this. Do macro definitions have a life-cycle in GDB?
> +static PyObject *
> +macropy_str (PyObject *self)
> +{
> + PyObject *result = PyString_FromString ("<gdb.Macro ");
> + macro_object *me = (macro_object *) self;
> + struct macro_definition *macro;
> + PyObject *argv = macropy_argv_method (self, Py_None);
> + PyObject *definition = macropy_definition_method (self, Py_None);
> + PyObject *include_trail = macropy_include_trail_method (self, Py_None);
> + PyObject *is_function_like = macropy_is_function_like_method (self, Py_None);
> + PyObject *name = PyString_FromString (me->name);
> +
> + if (!definition || !is_function_like || !argv
> + || !include_trail || !name || !result)
> + goto err_ret;
Because all of these are defined in the local block, what exception will
be returned on what failure? If RESULT fails, then ARGV, I think the
exception written for RESULT will be overwritten. I know it is a pain,
but I think you need to check each one.
> +static int
> +macropy_compare (PyObject *self, PyObject *o2)
> +{
> + PyObject *my_str = macropy_str (self);
> + int result;
> +
> + if (!my_str)
> + return -1;
> +
> + if (PyObject_TypeCheck (o2, ¯o_object_type))
> + {
> + PyObject *other_str = macropy_str (o2);
> +
> + if (other_str)
> + result = PyObject_Compare (my_str, other_str);
You need to cope with errors here. Use PyErr_Occurred() or
gdbpy_print_stack.
> + else
> + result = -1;
> +
> + Py_DECREF (my_str);
> + Py_XDECREF (other_str);
> + return result;
> + }
> +
> + result = PyObject_Compare (my_str, o2);
> +
Same as above.
> + Py_DECREF (my_str);
> + return result;
> +}
> +
> +static long
> +macropy_hash(PyObject *o)
> +{
> + long result;
> + PyObject *my_str = macropy_str (o);
> +
> + if (!my_str)
> + return -1;
> +
> + result = PyObject_Hash (my_str);
Same as above, re failure. Why do you need a custom hash function?
> + PyObject *tmp;
> +
> + if (! PyObject_TypeCheck (mud->list, &PyList_Type))
> + goto err_exit;
These comments are somewhat complex, and really follow the three paragraph
comments belows. So take them as one big comment.
Normally, a function that does not return a Python object has to
deal with stack printing/error detection internally. Because this is not
a Python function (IE returning NULL signifies an error, you have to
check and deal with the error here) this is done with
gdbpy_print_stack. However ....
> + if (mud->errors != 0)
> + goto err_exit;
> +
> + tmp = macropy_object_create (definition, name, source, line);
> + if (tmp)
> + {
> + if (PyList_Append (mud->list, tmp) != 0)
> + goto err_exit;
...
> +static PyObject *
> +stpy_macros (PyObject *self, PyObject *args)
> +{
> + struct symtab *st = symtab_object_to_symtab (self);
> + struct macro_user_data mud;
> + PyObject *result;
> +
> + STPY_REQUIRE_VALID (self, st);
> +
> + result = PyList_New (0);
> + if (result == NULL)
> + return NULL;
> +
> + mud.list = result;
> + mud.errors = 0;
> + macro_for_each (st->macro_table, add_macro_to_list, &mud);
> +
> + if (mud.errors != 0)
> + {
> + Py_DECREF (result);
> + return NULL;
I'm really puzzled what to do here. I'm assuming the iterator won't
stop because the helper function has encountered errors (at least, the
function pointer prototype is a void return). Because macro_for_each is
a GDB iterator function that calls (through a function pointer and other
GCC helpers) add_macro_to_list, which itself calls Python functions,
each iteration can raise a Python error. If we print and cope with the
error for each iteration in the helper as suggested above, the user will
see each exception and no exception data is overwritten. Also there
seems (to me, at least) no way to abort the iterator earlier.
OTOH we want to make sure that this function returns correctly,
according to how Python functions should. So we SHOULD return NULL here
if there were errors, but if we do what I suggested above, every
exception will already be printed and cleared. So returning NULL here
will cause Python to complain. But I also don't want previous iteration
exceptions overwritten either. Maybe your way is right in that we only
report the last iterations exception. Or maybe we should construct our
own exception and return that. I do not know. I'd appreciate
comments here from the maintainers.
> --- /dev/null
> +++ b/gdb/testsuite/gdb.python/py-macro.c
> @@ -0,0 +1,72 @@
> +#ifdef DEF_MACROS
Needs a copyright/license header.
> +#
> +# 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 macros to Python.
> +
> +if $tracelevel then {
> + strace $tracelevel
> +}
> +
> +load_lib gdb-python.exp
> +
> +set testfile "py-macro"
> +set srcfile ${testfile}.c
> +set binfile ${objdir}/${subdir}/${testfile}
> +get_compiler_info ${binfile}
> +if [test_compiler_info gcc*] {
> + lappend options additional_flags=-g3
> +} else {
> + untested ${testfile}.exp
> + return -1
> +}
> +
> +if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile} $options] } {
> + untested ${testfile}.exp
> + return -1
> +}
> +
> +# Start with a fresh gdb.
> +gdb_exit
> +gdb_start
> +gdb_reinitialize_dir $srcdir/$subdir
> +gdb_load ${binfile}
> +
> +# Skip all tests if Python scripting is not enabled.
> +if { [skip_python_tests] } { continue }
This is something we should do for all Python tests, so I am not just
singling this one out. But we should probably make this test earlier.
Thanks for writing this patch. I am really looking forward to it being
checked in when it is approved!
Cheers,
Phil
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [patch] PR python/10807 API for macros.
2011-08-16 10:02 ` Phil Muldoon
@ 2011-08-16 15:16 ` Matt Rice
2011-08-17 11:10 ` Phil Muldoon
0 siblings, 1 reply; 5+ messages in thread
From: Matt Rice @ 2011-08-16 15:16 UTC (permalink / raw)
To: pmuldoon; +Cc: gdb-patches
On Tue, Aug 16, 2011 at 3:01 AM, Phil Muldoon <pmuldoon@redhat.com> wrote:
> Matt Rice <ratmice@gmail.com> writes:
>
>> In the gdb.Macro class, I didn't used methods instead of attributes,
>> because the values are lazily computed/can return different objects if
>> called multiple times, thus attributes seemed weird, even though I
>> think the Macro methods are fairly attribute like. so if anyone has
>> any opinion here on attributes vs methods.
>
> Thanks.
>
>> +typedef struct
>> +{
>> + PyObject_HEAD;
>> + const char *name;
>> + struct macro_source_file *src_file;
>> + int src_line;
>> +} macro_object;
>> +
>> +static PyTypeObject macro_object_type;
>
> We've been trying to put comments in typedef/struct definitions to
> narrate each field. Some of the older Python files do not have them,
> but the newer ones do. Just a one line comment on each field is fine.
>
>> +static PyObject *
>> +argv_to_py (struct macro_definition *macro)
>> +{
>> + PyObject *ret = NULL;
>> +
>> + if (macro->kind == macro_function_like)
>> + {
>> + Py_ssize_t i;
>> + PyObject *ret = PyList_New (macro->argc);
>> +
>> + if (ret == NULL)
>> + return NULL;
>> +
>> + for (i = 0; i < macro->argc; i++)
>> + {
>> + PyObject *item = PyString_FromString (macro->argv[i]);
>> +
>> + if (!item)
>> + goto err_ret;
>> +
>> + if (PyList_SetItem (ret, i, item) != 0)
>> + goto err_ret;
>> + }
>> +
>> + return ret;
>> + }
>> +
>> + Py_RETURN_NONE;
>> +
>> + err_ret:
>> + Py_XDECREF (ret);
>> + return NULL;
>> +}
>
> Some minor nits.
>
> Some indents looks a little weird, but it might just be the mailer. You
> don't need Py_XDECREF here as you already accounted for the PyList being
> NULL. So just Py_DECREF.
>
>> +
>> +static PyObject *
>> +include_trail_to_py(struct macro_definition *macro,
>> + const char *name,
>> + struct macro_source_file *src_file,
>> + int src_line)
>> +{
>> + PyObject *tuple = PyTuple_New (2);
>> + PyObject *result = PyList_New (0);
>> + PyObject *tmp;
>> + struct macro_source_file *file;
>> + int line;
>> +
>> + if (!tuple || !result)
>> + goto err_exit;
>
> Even though either one of these indicates a failure case, and cannot
> continue, you need to check the individual return status right after
> instantiation. This is so you return the correct exception back to the
> user.
>
>> + file = macro_definition_location (src_file, src_line, name, &line);
>> + if (!file)
>> + goto err_exit;
>
> I'm not sure it is right to do this. macro_definition_location is a GDB
> function that can return NULL, not out of error, but because there is no
> definition of a splay tree node for that definition. Looking at the
> functions you call, and macro_definition_location calls, this can
> expected and does not signal a Python error condition. While, at this
> point it is right to exit because there is nothing more you can do,
> returning NULL from a Python function raises the expectation there is a
> Python error with an accompanying exception. If I remember correctly,
> Python will complain about this. Perhaps it would be better in this
> case to return None, and document the return strategy in the comments at
> the function header.
>
>> +
>> + tmp = PyString_FromString (file->filename);
>> + if (!tmp)
>> + goto err_exit;
>> + PyTuple_SetItem (tuple, 0, tmp);
>> +
>> + tmp = PyInt_FromLong (line);
>> + if (!tmp)
>> + goto err_exit;
>> + if (PyTuple_SetItem (tuple, 1, tmp) != 0)
>> + goto err_exit;
>> + if (PyList_Append (result, tuple) != 0)
>> + goto err_exit;
>> + Py_DECREF (tuple);
>
>> + while (file->included_by)
>> + {
>> + tuple = PyTuple_New (2);
>> +
>> + if (!tuple)
>> + goto err_exit;
>> +
>> + tmp = PyString_FromString (file->included_by->filename);
>> + if (!tmp)
>> + goto err_exit;
>> + if (PyTuple_SetItem (tuple, 0, tmp) != 0)
>> + goto err_exit;
>> +
>> + tmp = PyInt_FromLong (file->included_at_line);
>> + if (!tmp)
>> + goto err_exit;
>> + if (PyTuple_SetItem (tuple, 1, tmp) != 0)
>> + goto err_exit;
>> +
>> + if (PyList_Append (result, tuple) != 0)
>> + goto err_exit;
>> + Py_DECREF (tuple);
>> +
>> + file = file->included_by;
>> + }
>> +
>> + return result;
>> +
>> + err_exit:
>> + Py_XDECREF (tuple);
>> + Py_DECREF (result);
>
> Either of these could be NULL, so you need to use XDECREF in both cases.
>
>> + return NULL;
>> +}
>> +
>> +/* Create a new macro (gdb.Macro) object that encapsulates the
>> + macro_definition structure from GDB. */
>> +PyObject *
>> +macropy_object_create (struct macro_definition *macro,
>> + const char *name,
>> + struct macro_source_file *ms,
>> + int line)
>
> I normally try to document parameter usage here. And other publicly
> facing functions, too.
>
> +{
>> + macro_object *macro_obj;
>> +
>> + macro_obj = PyObject_New (macro_object, ¯o_object_type);
>> + if (macro_obj)
>> + {
>> + /* Save enough to lookup the macro again in the methods.
>> + Then do a lookup and lazily copy things into PyObjects.
>> + Consecutive lookups should be OK, because of the splay tree.
>> + We cannot save a macro definition due to inconsistent memory
>> + management. We rely on the fact that the macro_source_file
>> + is not released until exit.
>> +
>> + It seems we may need to move to using a 'macro_scope'
>> + if we want a python api for user-defined macros. */
>
>
> What do you mean by inconsistent memory management? Can you expand/explain
> this. Do macro definitions have a life-cycle in GDB?
depends on the source of the macro, xmalloc directly for user-defined,
or the macro table's obstack.
from macroscope.c:
/* A table of user-defined macros. Unlike the macro tables used for
symtabs, this one uses xmalloc for all its allocation, not an
obstack, and it doesn't bcache anything; it just xmallocs things. So
it's perfectly possible to remove things from this, or redefine
things. */
I'm second guessing myself that caching the macro_source file is safe though.
>> +static PyObject *
>> +macropy_str (PyObject *self)
>> +{
>> + PyObject *result = PyString_FromString ("<gdb.Macro ");
>> + macro_object *me = (macro_object *) self;
>> + struct macro_definition *macro;
>> + PyObject *argv = macropy_argv_method (self, Py_None);
>> + PyObject *definition = macropy_definition_method (self, Py_None);
>> + PyObject *include_trail = macropy_include_trail_method (self, Py_None);
>> + PyObject *is_function_like = macropy_is_function_like_method (self, Py_None);
>> + PyObject *name = PyString_FromString (me->name);
>> +
>> + if (!definition || !is_function_like || !argv
>> + || !include_trail || !name || !result)
>> + goto err_ret;
>
>
> Because all of these are defined in the local block, what exception will
> be returned on what failure? If RESULT fails, then ARGV, I think the
> exception written for RESULT will be overwritten. I know it is a pain,
> but I think you need to check each one.
>
>
>> +static int
>> +macropy_compare (PyObject *self, PyObject *o2)
>> +{
>> + PyObject *my_str = macropy_str (self);
>> + int result;
>> +
>> + if (!my_str)
>> + return -1;
>> +
>> + if (PyObject_TypeCheck (o2, ¯o_object_type))
>> + {
>> + PyObject *other_str = macropy_str (o2);
>> +
>> + if (other_str)
>> + result = PyObject_Compare (my_str, other_str);
>
> You need to cope with errors here. Use PyErr_Occurred() or
> gdbpy_print_stack.
>
>
>> + else
>> + result = -1;
>> +
>> + Py_DECREF (my_str);
>> + Py_XDECREF (other_str);
>> + return result;
>> + }
>> +
>> + result = PyObject_Compare (my_str, o2);
>> +
>
> Same as above.
>
>> + Py_DECREF (my_str);
>> + return result;
>> +}
>> +
>> +static long
>> +macropy_hash(PyObject *o)
>> +{
>> + long result;
>> + PyObject *my_str = macropy_str (o);
>> +
>> + if (!my_str)
>> + return -1;
>> +
>> + result = PyObject_Hash (my_str);
>
> Same as above, re failure. Why do you need a custom hash function?
needed the hash function for storing in sets
for the same reason that set([{"a" : "b"}]) doesn't work
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'
mostly I was just using it in the tests,
the only use case I can imagine is once we have a symtab_and_line
macros() function
you could use it to find the difference between 2 'macro scopes'
>> + PyObject *tmp;
>> +
>> + if (! PyObject_TypeCheck (mud->list, &PyList_Type))
>> + goto err_exit;
>
> These comments are somewhat complex, and really follow the three paragraph
> comments belows. So take them as one big comment.
>
> Normally, a function that does not return a Python object has to
> deal with stack printing/error detection internally. Because this is not
> a Python function (IE returning NULL signifies an error, you have to
> check and deal with the error here) this is done with
> gdbpy_print_stack. However ....
>
>> + if (mud->errors != 0)
>> + goto err_exit;
>> +
>> + tmp = macropy_object_create (definition, name, source, line);
>> + if (tmp)
>> + {
>> + if (PyList_Append (mud->list, tmp) != 0)
>> + goto err_exit;
>
> ...
>
>> +static PyObject *
>> +stpy_macros (PyObject *self, PyObject *args)
>> +{
>> + struct symtab *st = symtab_object_to_symtab (self);
>> + struct macro_user_data mud;
>> + PyObject *result;
>> +
>> + STPY_REQUIRE_VALID (self, st);
>> +
>> + result = PyList_New (0);
>> + if (result == NULL)
>> + return NULL;
>> +
>> + mud.list = result;
>> + mud.errors = 0;
>> + macro_for_each (st->macro_table, add_macro_to_list, &mud);
>> +
>> + if (mud.errors != 0)
>> + {
>> + Py_DECREF (result);
>> + return NULL;
>
> I'm really puzzled what to do here. I'm assuming the iterator won't
> stop because the helper function has encountered errors (at least, the
> function pointer prototype is a void return). Because macro_for_each is
> a GDB iterator function that calls (through a function pointer and other
> GCC helpers) add_macro_to_list, which itself calls Python functions,
> each iteration can raise a Python error. If we print and cope with the
> error for each iteration in the helper as suggested above, the user will
> see each exception and no exception data is overwritten. Also there
> seems (to me, at least) no way to abort the iterator earlier.
>
> OTOH we want to make sure that this function returns correctly,
> according to how Python functions should. So we SHOULD return NULL here
> if there were errors, but if we do what I suggested above, every
> exception will already be printed and cleared. So returning NULL here
> will cause Python to complain. But I also don't want previous iteration
> exceptions overwritten either. Maybe your way is right in that we only
> report the last iterations exception. Or maybe we should construct our
> own exception and return that. I do not know. I'd appreciate
> comments here from the maintainers.
another approach may be to use a 'macro iterator' method which calls a
python function with the converted arguments. I initially had
macro_object just copy everything into a python object. but the
macros() implementations used far too much memory (in a hello world)
building a full list with each macro containing an include_trail,
with a macro iterator we wouldn't return a list, just call the
callback for each macro, it'd mean error handling would have to happen
in said python callback function, but we'd be free of any concerns
with caching the macro_source_file, otoh memory size probably won't go
away it just means we have punted said concerns to the user, if they
try to store it in a list, they'd probably run into the same thing.
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.python/py-macro.c
>> @@ -0,0 +1,72 @@
>> +#ifdef DEF_MACROS
>
> Needs a copyright/license header.
>
>
>> +#
>> +# 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 macros to Python.
>> +
>> +if $tracelevel then {
>> + strace $tracelevel
>> +}
>> +
>> +load_lib gdb-python.exp
>> +
>> +set testfile "py-macro"
>> +set srcfile ${testfile}.c
>> +set binfile ${objdir}/${subdir}/${testfile}
>> +get_compiler_info ${binfile}
>> +if [test_compiler_info gcc*] {
>> + lappend options additional_flags=-g3
>> +} else {
>> + untested ${testfile}.exp
>> + return -1
>> +}
>> +
>> +if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile} $options] } {
>> + untested ${testfile}.exp
>> + return -1
>> +}
>> +
>> +# Start with a fresh gdb.
>> +gdb_exit
>> +gdb_start
>> +gdb_reinitialize_dir $srcdir/$subdir
>> +gdb_load ${binfile}
>> +
>> +# Skip all tests if Python scripting is not enabled.
>> +if { [skip_python_tests] } { continue }
>
> This is something we should do for all Python tests, so I am not just
> singling this one out. But we should probably make this test earlier.
>
> Thanks for writing this patch. I am really looking forward to it being
> checked in when it is approved!
>
> Cheers,
>
> Phil
>
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [patch] PR python/10807 API for macros.
2011-08-16 15:16 ` Matt Rice
@ 2011-08-17 11:10 ` Phil Muldoon
2011-08-17 13:44 ` Matt Rice
0 siblings, 1 reply; 5+ messages in thread
From: Phil Muldoon @ 2011-08-17 11:10 UTC (permalink / raw)
To: Matt Rice; +Cc: gdb-patches
Matt Rice <ratmice@gmail.com> writes:
>> What do you mean by inconsistent memory management? Can you expand/explain
>> this. Do macro definitions have a life-cycle in GDB?
>
> depends on the source of the macro, xmalloc directly for user-defined,
> or the macro table's obstack.
>
> from macroscope.c:
> /* A table of user-defined macros. Unlike the macro tables used for
> symtabs, this one uses xmalloc for all its allocation, not an
> obstack, and it doesn't bcache anything; it just xmallocs things. So
> it's perfectly possible to remove things from this, or redefine
> things. */
>
> I'm second guessing myself that caching the macro_source file is safe though.
Is there life-cycle management for macros? (See py-symbol.c for
life-cycle management of symbols). If so, we should invalidate (but keep
around) the macro Python object, but run a validity routine to check
that the macro exists.
If not, then I am not sure. If you cache the macro_source file, do you
keep it around forever? I am also unsure if it is ok to do this.
>> Same as above, re failure. Why do you need a custom hash function?
>
> needed the hash function for storing in sets
> for the same reason that set([{"a" : "b"}]) doesn't work
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> TypeError: unhashable type: 'dict'
>
> mostly I was just using it in the tests,
> the only use case I can imagine is once we have a symtab_and_line
> macros() function
> you could use it to find the difference between 2 'macro scopes'
That's totally fine, I just missed the usage in the testsuite.
>> These comments are somewhat complex, and really follow the three paragraph
>> comments belows. So take them as one big comment.
>>
...
>> Normally, a function that does not return a Python object has to
>> deal with stack printing/error detection internally. Because this is not
>> a Python function (IE returning NULL signifies an error, you have to
>> check and deal with the error here) this is done with
>> gdbpy_print_stack. However ....
...
>> I'm really puzzled what to do here. I'm assuming the iterator won't
>> stop because the helper function has encountered errors (at least, the
>> function pointer prototype is a void return). Because macro_for_each is
>> a GDB iterator function that calls (through a function pointer and other
>> GCC helpers) add_macro_to_list, which itself calls Python functions,
>> each iteration can raise a Python error. If we print and cope with the
>> error for each iteration in the helper as suggested above, the user will
>> see each exception and no exception data is overwritten. Also there
>> seems (to me, at least) no way to abort the iterator earlier.
>>
>> OTOH we want to make sure that this function returns correctly,
>> according to how Python functions should. So we SHOULD return NULL here
>> if there were errors, but if we do what I suggested above, every
>> exception will already be printed and cleared. So returning NULL here
>> will cause Python to complain. But I also don't want previous iteration
>> exceptions overwritten either. Maybe your way is right in that we only
>> report the last iterations exception. Or maybe we should construct our
>> own exception and return that. I do not know. I'd appreciate
>> comments here from the maintainers.
>
> another approach may be to use a 'macro iterator' method which calls a
> python function with the converted arguments. I initially had
> macro_object just copy everything into a python object. but the
> macros() implementations used far too much memory (in a hello world)
> building a full list with each macro containing an include_trail,
> with a macro iterator we wouldn't return a list, just call the
> callback for each macro, it'd mean error handling would have to happen
> in said python callback function, but we'd be free of any concerns
> with caching the macro_source_file, otoh memory size probably won't go
> away it just means we have punted said concerns to the user, if they
> try to store it in a list, they'd probably run into the same thing.
I think you and I are violently agreeing, but saying for effect
anyway. You might also call the iterator and store them temporarily in a
non-Python way (VEC, or something that suits your purpose). Then as a
second-pass, convert them to the Python object, error-checking as
appropriate?
Cheers,
Phil
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [patch] PR python/10807 API for macros.
2011-08-17 11:10 ` Phil Muldoon
@ 2011-08-17 13:44 ` Matt Rice
0 siblings, 0 replies; 5+ messages in thread
From: Matt Rice @ 2011-08-17 13:44 UTC (permalink / raw)
To: pmuldoon; +Cc: gdb-patches
On Wed, Aug 17, 2011 at 4:09 AM, Phil Muldoon <pmuldoon@redhat.com> wrote:
> Matt Rice <ratmice@gmail.com> writes:
>
>
>>> What do you mean by inconsistent memory management? Can you expand/explain
>>> this. Do macro definitions have a life-cycle in GDB?
>>
>> depends on the source of the macro, xmalloc directly for user-defined,
>> or the macro table's obstack.
>>
>> from macroscope.c:
>> /* A table of user-defined macros. Unlike the macro tables used for
>> symtabs, this one uses xmalloc for all its allocation, not an
>> obstack, and it doesn't bcache anything; it just xmallocs things. So
>> it's perfectly possible to remove things from this, or redefine
>> things. */
>>
>> I'm second guessing myself that caching the macro_source file is safe though.
>
> Is there life-cycle management for macros? (See py-symbol.c for
> life-cycle management of symbols). If so, we should invalidate (but keep
> around) the macro Python object, but run a validity routine to check
> that the macro exists.
Yeah, there is.
I was confused by free_macro_table != a destructor.
it seems to be there to discard macro_tables in the case of 'early retirement'
they can *poof* via obstack regardless of free_macro_table.
I'll just get rid of this for now, and do a deep copy of the macro on
initialization,
and see how that works with the macro iterator mechanism we discussed.
revisit it later when i can figure out how to get at the obstack, and
get it working correctly
with the obstackless macro tables, we can change this under the hood
without changing the API...
I suppose i should add an is_valid method that just returns True for
now with the deep copy.
> If not, then I am not sure. If you cache the macro_source file, do you
> keep it around forever? I am also unsure if it is ok to do this.
just as long as the macro_object *, so potentially forever.
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2011-08-17 13:44 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-08-16 6:36 [patch] PR python/10807 API for macros Matt Rice
2011-08-16 10:02 ` Phil Muldoon
2011-08-16 15:16 ` Matt Rice
2011-08-17 11:10 ` Phil Muldoon
2011-08-17 13:44 ` Matt Rice
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox