This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: [RFC - Python Scripting] New method gdb.Architecture.disassemble
Siva Chandra writes:
> The attached patch adds a new method 'disassemble' to the class
> gdb.Architecture. I have not yet added docs and tests, but will do so
> after I get feedback that adding such a method is OK.
Hi.
I like the idea, but for an API I wouldn't mind seeing something
a bit lower level. E.g., skip the higher level disassembler entry
points in gdb (mixed source/assembly support and all that), and provide
more direct access to the disassembler.
I didn't go through it with a fine toothed comb, but here are some questions.
1) Can we remove the py_out global?
2) It seems like this will export a lot of struct ui_out to the user.
I'd rather provide disassembly without having to commit to supporting
struct ui_out in Python.
Thoughts?
> 2013-02-04 Siva Chandra Reddy <sivachandra@google.com>
>
> Add a new method 'disassemble' to gdb.Architecture class.
> * Makefile.in: Add entries for the new file python/py-out.c
> * python/py-arch.c (archpy_disassmble): Implementation of the
> new method gdb.Architecture.disassemble.
> (arch_object_methods): Add entry for the new method.
> * python/py-out.c: Implementation of a Python ui_out.
> * python/python-internal.h: Add declarations for new utility
> functions.
> * python/python.c (_initialize_python): Initialize Python
> ui_out.
> diff --git a/gdb/Makefile.in b/gdb/Makefile.in
> index 68d545e..6be64cf 100644
> --- a/gdb/Makefile.in
> +++ b/gdb/Makefile.in
> @@ -291,6 +291,7 @@ SUBDIR_PYTHON_OBS = \
> py-lazy-string.o \
> py-newobjfileevent.o \
> py-objfile.o \
> + py-out.o \
> py-param.o \
> py-prettyprint.o \
> py-progspace.o \
> @@ -325,6 +326,7 @@ SUBDIR_PYTHON_SRCS = \
> python/py-lazy-string.c \
> python/py-newobjfileevent.c \
> python/py-objfile.c \
> + python/py-out.c \
> python/py-param.c \
> python/py-prettyprint.c \
> python/py-progspace.c \
> @@ -2129,6 +2131,10 @@ py-objfile.o: $(srcdir)/python/py-objfile.c
> $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-objfile.c
> $(POSTCOMPILE)
>
> +py-out.o: $(srcdir)/python/py-out.c
> + $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-out.c
> + $(POSTCOMPILE)
> +
> py-param.o: $(srcdir)/python/py-param.c
> $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-param.c
> $(POSTCOMPILE)
> diff --git a/gdb/python/py-arch.c b/gdb/python/py-arch.c
> index edd508f..b6f4f5a 100644
> --- a/gdb/python/py-arch.c
> +++ b/gdb/python/py-arch.c
> @@ -19,6 +19,7 @@
>
> #include "defs.h"
> #include "gdbarch.h"
> +#include "disasm.h"
> #include "arch-utils.h"
> #include "python-internal.h"
>
> @@ -86,6 +87,44 @@ archpy_name (PyObject *self, PyObject *args)
> return py_name;
> }
>
> +/* Implementation of Architecture.disassemble (low, high, [opcodes]) -> List.
> + Returns a list of instructions, each of which is a dictionary. */
> +
> +static PyObject *
> +archpy_disassemble (PyObject *self, PyObject *args)
> +{
> + struct gdbarch *gdbarch = arch_object_to_gdbarch (self);
> + CORE_ADDR low, high;
> + int opcodes = 0, flags = 0;
> + PyObject *result, *temp;
> + volatile struct gdb_exception except;
> +
> + if (!PyArg_ParseTuple (args, "KK|i", &low, &high, &opcodes))
> + return NULL;
> +
> + if (opcodes)
> + flags = DISASSEMBLY_RAW_INSN;
> +
> + TRY_CATCH (except, RETURN_MASK_ALL)
> + {
> + gdb_disassembly (gdbarch, py_out, NULL, flags, -1, low, high);
> + }
> + GDB_PY_HANDLE_EXCEPTION (except);
> +
> + temp = fetch_and_reset_py_out_object (py_out);
> + if (! (PyList_Check (temp) && PyList_Size (temp) > 0))
> + return NULL;
> +
> + /* gdb_disassembly puts a list of lists in py_out with the higher level list
> + containing a single item which is itself a list of instructions. Hence,
> + return the first element of the higher level list. */
> + result = PyList_GetItem (temp, 0);
> + Py_XINCREF (result);
> + Py_XDECREF (temp);
> +
> + return result;
> +}
> +
> /* Initializes the Architecture class in the gdb module. */
>
> void
> @@ -105,6 +144,9 @@ static PyMethodDef arch_object_methods [] = {
> { "name", archpy_name, METH_NOARGS,
> "name () -> String.\n\
> Return the name of the architecture as a string value." },
> + { "disassemble", archpy_disassemble, METH_VARARGS,
> + "name (low, high, [opcodes]) -> List.\n\
> +Return the list of instructions in the address range from LOW to HIGH." },
> {NULL} /* Sentinel */
> };
>
> diff --git a/gdb/python/py-out.c b/gdb/python/py-out.c
> new file mode 100644
> index 0000000..d278bc2
> --- /dev/null
> +++ b/gdb/python/py-out.c
> @@ -0,0 +1,259 @@
> +/* Python ui_out implementation.
> +
> + Copyright (C) 2013 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 "ui-out.h"
> +#include "python-internal.h"
> +
> +struct ui_out *py_out;
> +
> +struct row_data
> +{
> + /* DATA is either a list of rows, or just a dict. */
> + PyObject *data;
> +
> + /* The enclosing row for the above DATA. */
> + struct row_data *parent_row;
> +};
> +
> +/* This data structure captures the Python version of ui_out. The Python
> + version is not used to display output to a user, but to capture the results
> + from GDB's internals in to a Python data structure. Hence, it does not have
> + any representation for table headers. However, it can be viewed as a
> + recursive table structure wherin the highest level is a list of rows. All
> + rows in this list can either be a list themselves, or all of them can be
> + dicts holding the table's fields. If they were lists, then they follow the
> + same recurrsive structure as the higher levels.
> +
> + Example:
> +
> + [ # Highest level list which has two lists for rows
> + [ # Inner level row which is a list of lists
> + [ # Inner level row which is a list of dicts
> + {'a': 1, 'b': 2}, # Leaf row which is a dict
> + {'a': 3, 'b': 4}, # Leaf row which is a dict
> + ],
> +
> + [ # Inner level row which is a list of dicts
> + {'x': 5, 'y': 6}, # Leaf row which is a dict
> + {'x': 7, 'y': 8}, # Leaf row which is a dict
> + ],
> + ],
> +
> + [ # Inner level row which is list of dicts
> + {'p': 1, 'q': 2}, # Leaf row which is a dict
> + {'p': 3, 'q': 4}, # Leaf row which is a dict
> + ],
> + ]
> +*/
> +
> +struct py_out_data
> +{
> + /* The highest level list of rows. */
> + struct row_data *table;
> +
> + /* The current row that is being added to the table. */
> + struct row_data *current_row;
> +};
> +
> +static struct row_data *
> +new_row (struct row_data *parent)
> +{
> + struct row_data *row;
> +
> + row = (struct row_data *) xmalloc (sizeof (struct row_data));
> + row->data = NULL;
> + row->parent_row = parent;
> +
> + return row;
> +}
> +
> +PyObject *
> +fetch_and_reset_py_out_object (struct ui_out *ui_out)
> +{
> + PyObject *temp;
> + struct py_out_data *py_out_data = ui_out_data (ui_out);
> +
> + /* Ensure that the py_out object is complete. */
> + if (py_out_data->current_row != py_out_data->table)
> + internal_error (__FILE__, __LINE__,
> + _("Trying to fetch an incomplete Python ui_out object"));
> +
> + temp = py_out_data->table->data;
> + py_out_data->table->data = PyList_New (0);
> +
> + return temp;
> +}
> +
> +static void
> +py_out_row_begin (struct ui_out *ui_out, enum ui_out_type type, int level,
> + const char *id)
> +{
> + struct py_out_data *py_out_data = ui_out_data (ui_out);
> +
> + if (py_out_data->current_row)
> + {
> + if (py_out_data->current_row->data)
> + {
> + if (PyDict_Check (py_out_data->current_row->data))
> + /* If the row has data, check that it is not a dict first. */
> + internal_error (__FILE__, __LINE__,
> + _("Trying to add a row to a row which has "
> + "fields."));
> + else if (PyList_Check (py_out_data->current_row->data))
> + {
> + /* If the row is already a list, add a new row. */
> + struct row_data *new_row_data;
> +
> + new_row_data = new_row (py_out_data->current_row);
> + py_out_data->current_row = new_row_data;
> + }
> + else
> + /* If it is neither a list or a dict, then something has gone wrong
> + somewhere. */
> + internal_error (__FILE__, __LINE__,
> + _("Unexpected internal state in creating Python "
> + "ui_out object."));
> + }
> + else
> + {
> + /* Make the current row a list and add a new row. */
> + struct row_data *new_row_data;
> +
> + py_out_data->current_row->data = PyList_New (0);
> + new_row_data = new_row (py_out_data->current_row);
> + py_out_data->current_row = new_row_data;
> + }
> + }
> + else
> + {
> + /* This should never happen. */
> + internal_error (__FILE__, __LINE__,
> + _("Unexpected internal state in creating Python ui_out "
> + "object."));
> + }
> +}
> +
> +static void
> +py_out_row_end (struct ui_out *ui_out, enum ui_out_type type, int level)
> +{
> + struct py_out_data *py_out_data = ui_out_data (ui_out);
> + struct row_data *temp;
> +
> + /* If nothing was added to current row, then make it Py_None. */
> + if (py_out_data->current_row->data == NULL)
> + {
> + Py_INCREF (Py_None);
> + py_out_data->current_row->data = Py_None;
> + }
> +
> + /* Commit the row to the parent list. */
> + PyList_Append (py_out_data->current_row->parent_row->data,
> + py_out_data->current_row->data);
> +
> + /* Move up a level by making the parent row as the current row and free the
> + row_data object corresponding to current_row. */
> + temp = py_out_data->current_row;
> + py_out_data->current_row = py_out_data->current_row->parent_row;
> + xfree (temp);
> +}
> +
> +#define CHECK_AND_INIT_FIELD_ROW_DATA(data) \
> + do { \
> + if (!(data)) \
> + (data) = PyDict_New (); \
> + else \
> + { \
> + if (!PyDict_Check ((data))) \
> + internal_error (__FILE__, __LINE__, \
> + _("Adding fields to a row which is not a field " \
> + "row.")); \
> + } \
> + } while (0)
> +
> +static void
> +py_out_field_int (struct ui_out * ui_out, int fldno, int width,
> + enum ui_align align, const char *fldname, int value)
> +{
> + struct py_out_data *py_out_data = ui_out_data (ui_out);
> +
> + CHECK_AND_INIT_FIELD_ROW_DATA (py_out_data->current_row->data);
> +
> + PyDict_SetItemString (py_out_data->current_row->data, fldname,
> + PyInt_FromLong (value));
> +}
> +
> +static void
> +py_out_field_skip (struct ui_out *ui_out, int fldno, int width,
> + enum ui_align align, const char *fldname)
> +{
> + struct py_out_data *py_out_data = ui_out_data (ui_out);
> +
> + CHECK_AND_INIT_FIELD_ROW_DATA (py_out_data->current_row->data);
> +
> + Py_INCREF (Py_None);
> + PyDict_SetItemString (py_out_data->current_row->data, fldname,
> + Py_None);
> +}
> +
> +static void
> +py_out_field_string (struct ui_out * ui_out, int fldno, int width,
> + enum ui_align align, const char *fldname, const char *str)
> +{
> + struct py_out_data *py_out_data = ui_out_data (ui_out);
> +
> + CHECK_AND_INIT_FIELD_ROW_DATA (py_out_data->current_row->data);
> +
> + PyDict_SetItemString (py_out_data->current_row->data, fldname,
> + PyString_FromString (str));
> +}
> +
> +static struct ui_out_impl py_ui_out_impl =
> +{
> + 0, /* table_begin */
> + 0, /* table_body */
> + 0, /* table_end */
> + 0, /* table_header */
> + py_out_row_begin, /* begin */
> + py_out_row_end, /* end */
> + py_out_field_int, /* field_int */
> + py_out_field_skip, /* field_skip */
> + py_out_field_string, /* field_string */
> + 0, /* field_fmt */
> + 0, /* space */
> + 0, /* text */
> + 0, /* message */
> + 0, /* wrap_hint */
> + 0, /* flush */
> + 0, /* redirect */
> + 0 /* is_mi_like_p */
> +};
> +
> +void
> +gdbpy_initialize_py_out (void)
> +{
> + struct py_out_data *py_out_data;
> +
> + py_out_data = (struct py_out_data *) xmalloc (sizeof (struct py_out_data));
> + py_out_data->table = new_row (NULL);
> + py_out_data->table->data = PyList_New (0);
> + py_out_data->current_row = py_out_data->table;
> +
> + py_out = ui_out_new (&py_ui_out_impl, py_out_data, 0);
> +}
> diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
> index 8dff1d7..d852d03 100644
> --- a/gdb/python/python-internal.h
> +++ b/gdb/python/python-internal.h
> @@ -143,6 +143,7 @@ struct language_defn;
> struct program_space;
> struct bpstats;
> struct inferior;
> +struct ui_out;
>
> extern PyObject *gdb_module;
> extern PyObject *gdb_python_module;
> @@ -267,6 +268,8 @@ struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);
> struct frame_info *frame_object_to_frame_info (PyObject *frame_obj);
> struct gdbarch *arch_object_to_gdbarch (PyObject *obj);
>
> +PyObject *fetch_and_reset_py_out_object (struct ui_out *);
> +
> void gdbpy_initialize_gdb_readline (void);
> void gdbpy_initialize_auto_load (void);
> void gdbpy_initialize_values (void);
> @@ -297,6 +300,7 @@ void gdbpy_initialize_exited_event (void);
> void gdbpy_initialize_thread_event (void);
> void gdbpy_initialize_new_objfile_event (void);
> void gdbpy_initialize_arch (void);
> +void gdbpy_initialize_py_out (void);
>
> struct cleanup *make_cleanup_py_decref (PyObject *py);
>
> @@ -305,6 +309,7 @@ struct cleanup *ensure_python_env (struct gdbarch *gdbarch,
>
> extern struct gdbarch *python_gdbarch;
> extern const struct language_defn *python_language;
> +extern struct ui_out *py_out;
>
> /* Use this after a TRY_EXCEPT to throw the appropriate Python
> exception. */
> diff --git a/gdb/python/python.c b/gdb/python/python.c
> index 53ddee9..3ab4b7c 100644
> --- a/gdb/python/python.c
> +++ b/gdb/python/python.c
> @@ -1621,6 +1621,7 @@ message == an error message without a stack will be printed."),
> gdbpy_initialize_thread_event ();
> gdbpy_initialize_new_objfile_event () ;
> gdbpy_initialize_arch ();
> + gdbpy_initialize_py_out ();
>
> observer_attach_before_prompt (before_prompt_hook);
>
--
/dje