From: Jan Vrany Date: Thu, 21 Nov 2024 12:31:21 +0000 (+0000) Subject: gdb/python: allow instantiation of gdb.Block from Python X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=3150cb09a4d66e71686899207907f5b491fc39e6;p=thirdparty%2Fbinutils-gdb.git gdb/python: allow instantiation of gdb.Block from Python This commit adds code to allow user extension to instantiate gdb.Block. This is a step towards a Python support for dynamically generated code (JIT) in GDB. Reviewed-By: Eli Zaretskii --- diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index 3fb688c597e..b2fb86091b3 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -6037,6 +6037,12 @@ historical compatibility. A @code{gdb.Block} object has the following methods: +@defun Block.__init__ (superblock, start, end) +Create new block in @var{superblock} spanning from @var{start} to @var{end}. +The new block's @var{start}--@var{end} range must be within superblock's +range and must not overlap with any block already contained in superblock. +@end defun + @defun Block.is_valid () Returns @code{True} if the @code{gdb.Block} object is valid, @code{False} if not. A block object can become invalid if the block it diff --git a/gdb/python/py-block.c b/gdb/python/py-block.c index a05a2796c52..626ed10deb6 100644 --- a/gdb/python/py-block.c +++ b/gdb/python/py-block.c @@ -342,6 +342,106 @@ blpy_dealloc (PyObject *obj) Py_TYPE (obj)->tp_free (obj); } +/* Object initializer; creates new block. + + Use: __init__(SUPERBLOCK, START, END). */ + +static int +blpy_init (PyObject *zelf, PyObject *args, PyObject *kw) +{ + struct block_object *self = (struct block_object*) zelf; + + if (self->block) + { + PyErr_Format (PyExc_RuntimeError, + _("Block object already initialized.")); + return -1; + } + + static const char *keywords[] = { "superblock", "start", "end", NULL }; + PyObject *superblock_obj; + uint64_t start; + uint64_t end; + + if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "OKK", keywords, + &superblock_obj, &start, &end)) + return -1; + + + auto superblock = block_object_to_block (superblock_obj); + if (superblock == nullptr) + { + PyErr_Format (PyExc_TypeError, + _("The superblock argument is not valid gdb.Block " + "object")); + return -1; + } + + /* Check that start-end range is valid. */ + if (! (start <= end)) + { + PyErr_Format (PyExc_ValueError, + _("The start argument must be less or equal to the end " + "argument")); + return -1; + + } + + /* Check that start-end range is within superblocks' range. */ + if (! (superblock-> start() <= start && end <= superblock->end ())) + { + PyErr_Format (PyExc_ValueError, + _("The start-end range must be within superblocks' " + "range")); + return -1; + } + + /* Check that start-end range does not overlap with any + "sibling" blocks' range. */ + auto cu = superblock->global_block ()->compunit (); + + for (auto each : cu->blockvector ()->blocks ()) + { + if (each->superblock () == superblock) + { + /* each is a "sibling" block. */ + if (std::max (start, each->start ()) < std::min(end, each->end ())) + { + PyErr_Format (PyExc_ValueError, + _("The start-end range overlaps with one of the " + "sibling blocks")); + return -1; + } + } + } + + auto obstack = &(cu->objfile ()->objfile_obstack); + auto blk = new (obstack) block (); + + blk->set_superblock (superblock); + blk->set_multidict (mdict_create_linear (obstack, NULL)); + blk->set_start ((CORE_ADDR) start); + blk->set_end ((CORE_ADDR) end); + + cu->blockvector ()->add_block (blk); + + self->block = blk; + self->objfile = cu->objfile (); + + htab_t table = blpy_objfile_data_key.get (self->objfile); + if (table == nullptr) + { + table = htab_create_alloc (10, block_object_hash, block_object_eq, + block_object_del, xcalloc, xfree); + blpy_objfile_data_key.set (self->objfile, table); + } + hashval_t hash = htab_hash_pointer (blk); + void **slot = htab_find_slot_with_hash (table, blk, hash, INSERT); + *slot = self; + + return 0; +} + /* Create a new block object (gdb.Block) that encapsulates the struct block object from GDB. */ PyObject * @@ -533,7 +633,6 @@ blpy_richcompare (PyObject *self, PyObject *other, int op) static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION gdbpy_initialize_blocks (void) { - block_object_type.tp_new = PyType_GenericNew; if (gdbpy_type_ready (&block_object_type) < 0) return -1; @@ -613,7 +712,15 @@ PyTypeObject block_object_type = { 0, /* tp_iternext */ block_object_methods, /* tp_methods */ 0, /* tp_members */ - block_object_getset /* tp_getset */ + block_object_getset, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + blpy_init, /* tp_init */ + 0, /* tp_alloc */ + PyType_GenericNew /* tp_new */ }; static PyMethodDef block_iterator_object_methods[] = { diff --git a/gdb/testsuite/gdb.python/py-block.exp b/gdb/testsuite/gdb.python/py-block.exp index 64ee12786c4..3c5d291edb0 100644 --- a/gdb/testsuite/gdb.python/py-block.exp +++ b/gdb/testsuite/gdb.python/py-block.exp @@ -115,6 +115,29 @@ gdb_test "python print (repr (block))" "" \ "Check Frame 2's block not None" gdb_test "python print (block.function)" "main" "main block" +# Test creation of blocks. For that we create a new compunit to make sure +# there's space for new blocks to fit in. +gdb_py_test_silent_cmd "python cu = gdb.Compunit(\"dynamic\", gdb.current_progspace().objfiles()\[0\], 100, 200)" \ + "Create new compunit" 1 +gdb_test "python print ( gdb.Block(cu.static_block(), 100, 150))" \ + " \{.*\}>" \ + "Create new block" +gdb_test "python print ( gdb.Block(\"xxx\", 160, 170))" \ + "TypeError.*:.*" \ + "Try create new block with non-block superblock" +gdb_test "python print ( gdb.Block(cu.static_block(), 170, 160))" \ + "ValueError.*:.*" \ + "Try create new block with start > end" +gdb_test "python print ( gdb.Block(cu.static_block(), 70, 160))" \ + "ValueError.*:.*" \ + "Try create new block with outside superblock" +gdb_test "python print ( gdb.Block(cu.static_block(), 140, 160))" \ + "ValueError.*:.*" \ + "Try create new block overlaping with sibling" +gdb_test "python print ( gdb.Block(cu.static_block(), 160, 170))" \ + " \{.*\}>" \ + "Create sibling block" + # Test Block is_valid. This must always be the last test in this # testcase as it unloads the object file. delete_breakpoints