[RFC - Python Scripting] New method gdb.Architecture.disassemble

Siva Chandra sivachandra@google.com
Wed Feb 6 01:53:00 GMT 2013


On Tue, Feb 5, 2013 at 3:28 PM, Doug Evans <dje@google.com> wrote:
> 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.

The only useful entry point currently available is gdb_disassembly and
I do not think it is a bad entry point. Other disassembly functions in
disasm.c are static. However, for the Python API, my patch provides
only one option of whether to include or exclude opcodes in the
disassembled output.

> I didn't go through it with a fine toothed comb, but here are some questions.
> 1) Can we remove the py_out global?

At what level do you not want this to be global? I have made it static
to the file in the attached patch.

> 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.

I am not very sure I understand this point. My patch does not expose
anything about the struct ui_out to the user/Python API. Python ui_out
is only a way to get results from GDB internals into a Python data
structure. Also, this Python data structure does not depend on
gdb_disassembly's display format.

2013-02-05  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.
-------------- next part --------------
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..b1608d7 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,45 @@ 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;
+  struct ui_out *py_out = py_ui_out ();
+  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 +145,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..f6a1456
--- /dev/null
+++ b/gdb/python/py-out.c
@@ -0,0 +1,265 @@
+/* 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"
+
+static 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);
+}
+
+struct ui_out *
+py_ui_out (void)
+{
+  return py_out;
+}
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 8dff1d7..cbc9f31 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,9 @@ 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 *);
+struct ui_out *py_ui_out (void);
+
 void gdbpy_initialize_gdb_readline (void);
 void gdbpy_initialize_auto_load (void);
 void gdbpy_initialize_values (void);
@@ -297,6 +301,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);
 
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);
 


More information about the Gdb-patches mailing list