public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [RFC v2 00/21] Add Python "JIT" API
@ 2024-11-21 12:46 Jan Vrany
  2024-11-21 12:46 ` [RFC v2 01/21] gdb: update is_addr_in_objfile to support "dynamic" objfiles Jan Vrany
                   ` (20 more replies)
  0 siblings, 21 replies; 39+ messages in thread
From: Jan Vrany @ 2024-11-21 12:46 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany

Hello,

this is a v2 of RFC series that extends GDB's Python API to allow
interfacing with JIT compilers in Python.

The v1 submission is here:

   https://inbox.sourceware.org/gdb-patches/20241111145522.560028-1-jan.vrany@labware.com/

Changes in V2:

  * addressed all Eli's comments to documentation parts
  * use nullptr instead of NULL thorough this series
  * avoid using auto in cases Andrew mentioned, also
    thorough this series.
  * updated commit message for commit
    "gdb/python: add subblocks property to gdb.Block"

Thanks,

Jan

Jan Vrany (21):
  gdb: update is_addr_in_objfile to support "dynamic" objfiles
  gdb/python: add subblocks property to gdb.Block
  gdb/python: add domain property to gdb.Symbol
  gdb/python: add void_type () method to gdb.Architecture object
  gdb/python: add function () method to gdb.Type object
  gdb/python: add template function to implement equality comparison
  gdb/python: make gdb.Symbol comparable for equality
  gdb/python: make gdb.Symtab comparable for equality
  gdb: use std::vector<> to hold on blocks in struct blockvector
  gdb/python: add gdb.Compunit
  gdb/python: allow instantiation of gdb.Objfile from Python
  gdb/python: add unlink () method to gdb.Objfile object
  gdb/python: allow instantiation of gdb.Compunit from Python
  gdb/python: allow instantiation of gdb.Symtab from Python
  gdb/python: allow instantiation of gdb.Block from Python
  gdb/python: allow instantiation of gdb.Symbol from Python
  gdb/python: add add_symbol () method to gdb.Block
  gdb/python: add more attributes to gdb.LinetableEntry objects
  gdb/python: allow instantiation of gdb.LineTableEntry objects
  gdb/python: allow instantiation of gdb.LineTable objects
  gdb/python: add section in documentation on implementing JIT interface

 gdb/Makefile.in                           |   1 +
 gdb/NEWS                                  |  17 +
 gdb/block.c                               |  59 +++
 gdb/block.h                               |  56 ++-
 gdb/buildsym.c                            |   6 +-
 gdb/doc/gdb.texinfo                       |   3 +-
 gdb/doc/python.texi                       | 322 +++++++++++++++++
 gdb/jit.c                                 |   8 +-
 gdb/mdebugread.c                          |  32 +-
 gdb/objfiles.c                            |  36 ++
 gdb/objfiles.h                            |   4 +
 gdb/python/py-arch.c                      |  16 +
 gdb/python/py-block.c                     | 196 +++++++++-
 gdb/python/py-compunit.c                  | 414 ++++++++++++++++++++++
 gdb/python/py-inferior.c                  |  10 +
 gdb/python/py-linetable.c                 | 243 ++++++++++++-
 gdb/python/py-objfile.c                   | 170 ++++++++-
 gdb/python/py-symbol.c                    | 153 +++++++-
 gdb/python/py-symtab.c                    |  70 +++-
 gdb/python/py-type.c                      |  54 +++
 gdb/python/python-internal.h              |  38 ++
 gdb/testsuite/gdb.base/jit-reader.exp     |   9 +
 gdb/testsuite/gdb.python/py-arch.exp      |   4 +
 gdb/testsuite/gdb.python/py-block.exp     |  49 +++
 gdb/testsuite/gdb.python/py-compunit.exp  | 102 ++++++
 gdb/testsuite/gdb.python/py-jit.c         |  61 ++++
 gdb/testsuite/gdb.python/py-jit.exp       |  57 +++
 gdb/testsuite/gdb.python/py-jit.py        | 110 ++++++
 gdb/testsuite/gdb.python/py-linetable.exp |  67 +++-
 gdb/testsuite/gdb.python/py-objfile.exp   |  50 ++-
 gdb/testsuite/gdb.python/py-symbol.exp    |  35 ++
 gdb/testsuite/gdb.python/py-symtab.exp    |  44 +++
 gdb/testsuite/gdb.python/py-type.exp      |  20 ++
 gdbsupport/gdb_obstack.h                  |  52 +++
 34 files changed, 2482 insertions(+), 86 deletions(-)
 create mode 100644 gdb/python/py-compunit.c
 create mode 100644 gdb/testsuite/gdb.python/py-compunit.exp
 create mode 100644 gdb/testsuite/gdb.python/py-jit.c
 create mode 100644 gdb/testsuite/gdb.python/py-jit.exp
 create mode 100644 gdb/testsuite/gdb.python/py-jit.py

-- 
2.45.2


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [RFC v2 01/21] gdb: update is_addr_in_objfile to support "dynamic" objfiles
  2024-11-21 12:46 [RFC v2 00/21] Add Python "JIT" API Jan Vrany
@ 2024-11-21 12:46 ` Jan Vrany
  2024-11-21 12:46 ` [RFC v2 02/21] gdb/python: add subblocks property to gdb.Block Jan Vrany
                   ` (19 subsequent siblings)
  20 siblings, 0 replies; 39+ messages in thread
From: Jan Vrany @ 2024-11-21 12:46 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany

While working with objfiles in Python I noticed that
gdb.Progspace.objfile_for_address () does not return "dynamic" objfile
create by (for example) GDB's JIT reader API.

This is because is_addr_in_objfile() checks if given address falls into
any (mappped) section of that objfile. However objfiles created by JIT
reader API do not have sections.

To solve this issue, this commit updates is_addr_in_objfile() to also
check if address fall into any compunit if that objfile. It does so only
if objfile has no sections.
---
 gdb/objfiles.c                        | 16 ++++++++++++++++
 gdb/testsuite/gdb.base/jit-reader.exp |  9 +++++++++
 2 files changed, 25 insertions(+)

diff --git a/gdb/objfiles.c b/gdb/objfiles.c
index 555195dc61f..0bb578fa6a8 100644
--- a/gdb/objfiles.c
+++ b/gdb/objfiles.c
@@ -1194,6 +1194,22 @@ is_addr_in_objfile (CORE_ADDR addr, const struct objfile *objfile)
       if (osect->contains (addr))
 	return true;
     }
+  /* Objfiles created dynamically by JIT reader API (and possibly by
+     other means too) do not have sections and therefore the above
+     check never succeeds.
+
+     For such "dynamic" objfiles walk over all compunits and check
+     if given ADDR falls into compunit's global block.  */
+  if (! objfile->sections_start)
+    {
+      for (compunit_symtab *cu
+	     : const_cast<struct objfile *>(objfile)->compunits ())
+	{
+	  global_block *gb = cu->blockvector ()->global_block ();
+	  if (gb->start () <= addr && addr <= gb->end ())
+	    return true;
+	}
+    }
   return false;
 }
 
diff --git a/gdb/testsuite/gdb.base/jit-reader.exp b/gdb/testsuite/gdb.base/jit-reader.exp
index 62f6af29ac1..9a873c77909 100644
--- a/gdb/testsuite/gdb.base/jit-reader.exp
+++ b/gdb/testsuite/gdb.base/jit-reader.exp
@@ -229,6 +229,15 @@ proc jit_reader_test {} {
 		gdb_test "python print( \[o for o in gdb.objfiles() if o.filename.startswith('<< JIT compiled code')\]\[0\].build_id )" \
 		    "None" \
 		    "python gdb.Objfile.build_id"
+
+		# Check that Progspace.objfile_for_address () finds "jitted"
+		# objfile
+		gdb_test "frame 0" \
+		    "#0  $hex in jit_function_stack_mangle ()$any" \
+		    "select frame 0"
+		gdb_test "python print( gdb.current_progspace().objfile_for_address(gdb.parse_and_eval('\$pc')) )" \
+		    "<gdb.Objfile filename=<< JIT compiled code at $hex >>>" \
+		    "python gdb.Progspace.objfile_for_address"
 	    }
 	}
     }
-- 
2.45.2


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [RFC v2 02/21] gdb/python: add subblocks property to gdb.Block
  2024-11-21 12:46 [RFC v2 00/21] Add Python "JIT" API Jan Vrany
  2024-11-21 12:46 ` [RFC v2 01/21] gdb: update is_addr_in_objfile to support "dynamic" objfiles Jan Vrany
@ 2024-11-21 12:46 ` Jan Vrany
  2024-11-21 13:35   ` Eli Zaretskii
  2024-11-21 12:46 ` [RFC v2 03/21] gdb/python: add domain property to gdb.Symbol Jan Vrany
                   ` (18 subsequent siblings)
  20 siblings, 1 reply; 39+ messages in thread
From: Jan Vrany @ 2024-11-21 12:46 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany, Eli Zaretskii

This commit adds new propery "subblocks" to gdb.Block objects. This
allows Python to traverse block tree starting with global block.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
 gdb/NEWS                              |  3 +++
 gdb/doc/python.texi                   |  5 +++++
 gdb/python/py-block.c                 | 30 +++++++++++++++++++++++++++
 gdb/testsuite/gdb.python/py-block.exp |  4 ++++
 4 files changed, 42 insertions(+)

diff --git a/gdb/NEWS b/gdb/NEWS
index 046daad0eae..c9e0439645f 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -83,6 +83,9 @@
   ** The 'qualified' argument to gdb.Breakpoint constructor will no
      longer accept non-bool types.
 
+  ** Added gdb.Block.subblocks.  Returns a list of blocks contained in that
+     block.
+
 * Debugger Adapter Protocol changes
 
   ** The "scopes" request will now return a scope holding global
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index cc455483166..290e9fea62f 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -6040,6 +6040,11 @@ The block containing this block.  If this parent block does not exist,
 this attribute holds @code{None}.  This attribute is not writable.
 @end defvar
 
+@defvar Block.subblocks
+A list of blocks nested in this block.  If there are no blocks nested,
+this attribute holds an empty list.  This attribute is not writable.
+@end defvar
+
 @defvar Block.global_block
 The global block associated with this block.  This attribute is not
 writable.
diff --git a/gdb/python/py-block.c b/gdb/python/py-block.c
index aeb9acb7260..1c5eab44b3a 100644
--- a/gdb/python/py-block.c
+++ b/gdb/python/py-block.c
@@ -149,6 +149,34 @@ blpy_get_superblock (PyObject *self, void *closure)
   Py_RETURN_NONE;
 }
 
+static PyObject *
+blpy_get_subblocks (PyObject *self, void *closure)
+{
+  const struct block *block;
+
+  BLPY_REQUIRE_VALID (self, block);
+
+  gdbpy_ref<> list (PyList_New (0));
+  if (list == nullptr)
+    return nullptr;
+
+  compunit_symtab *cu = block->global_block ()->compunit ();
+
+  for (const struct block *each : cu->blockvector ()->blocks ())
+    {
+      if (each->superblock () == block)
+	{
+	  gdbpy_ref<> item (block_to_block_object (each, cu->objfile ()));
+
+	  if (item.get () == nullptr
+	      || PyList_Append (list.get (), item.get ()) == -1)
+	    return nullptr;
+	}
+    }
+
+  return list.release ();
+}
+
 /* Return the global block associated to this block.  */
 
 static PyObject *
@@ -529,6 +557,8 @@ static gdb_PyGetSetDef block_object_getset[] = {
     "Whether this block is a static block.", NULL },
   { "is_global", blpy_is_global, NULL,
     "Whether this block is a global block.", NULL },
+  { "subblocks", blpy_get_subblocks, nullptr,
+    "List of blocks contained in this block.", nullptr },
   { NULL }  /* Sentinel */
 };
 
diff --git a/gdb/testsuite/gdb.python/py-block.exp b/gdb/testsuite/gdb.python/py-block.exp
index 0e6851ddf8b..20f21711126 100644
--- a/gdb/testsuite/gdb.python/py-block.exp
+++ b/gdb/testsuite/gdb.python/py-block.exp
@@ -61,6 +61,10 @@ gdb_py_test_silent_cmd "python sblock = block.static_block" \
     "Get block, static_block" 1
 gdb_test "python print (gblock.is_global)" "True" "is the global block"
 gdb_test "python print (sblock.is_static)" "True" "is the static block"
+gdb_test "python print (len(gblock.subblocks) > 0)" "True" \
+	"global block contains at least one block"
+gdb_test "python print (sblock in gblock.subblocks)" "True" \
+	"global block contains at static block"
 
 # Move up superblock(s) until we reach function block_func.
 gdb_test_no_output "python block = block.superblock" "get superblock"
-- 
2.45.2


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [RFC v2 03/21] gdb/python: add domain property to gdb.Symbol
  2024-11-21 12:46 [RFC v2 00/21] Add Python "JIT" API Jan Vrany
  2024-11-21 12:46 ` [RFC v2 01/21] gdb: update is_addr_in_objfile to support "dynamic" objfiles Jan Vrany
  2024-11-21 12:46 ` [RFC v2 02/21] gdb/python: add subblocks property to gdb.Block Jan Vrany
@ 2024-11-21 12:46 ` Jan Vrany
  2024-11-21 13:32   ` Eli Zaretskii
  2024-11-21 12:46 ` [RFC v2 04/21] gdb/python: add void_type () method to gdb.Architecture object Jan Vrany
                   ` (17 subsequent siblings)
  20 siblings, 1 reply; 39+ messages in thread
From: Jan Vrany @ 2024-11-21 12:46 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany, Eli Zaretskii

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
 gdb/NEWS                               |  2 ++
 gdb/doc/python.texi                    |  5 +++++
 gdb/python/py-symbol.c                 | 11 +++++++++++
 gdb/testsuite/gdb.python/py-symbol.exp |  1 +
 4 files changed, 19 insertions(+)

diff --git a/gdb/NEWS b/gdb/NEWS
index c9e0439645f..1ea9fcc65b9 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -86,6 +86,8 @@
   ** Added gdb.Block.subblocks.  Returns a list of blocks contained in that
      block.
 
+  ** Added gdb.Symbol.domain.  Contains the domain of the symbol.
+
 * Debugger Adapter Protocol changes
 
   ** The "scopes" request will now return a scope holding global
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 290e9fea62f..09e374700a4 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -6203,6 +6203,11 @@ of a symbol.  Each address class is a constant defined in the
 @code{gdb} module and described later in this chapter.
 @end defvar
 
+@defvar Symbol.domain
+The domain of the symbol.  Each domain is a constant defined in the
+@code{gdb} module and described later in this chapter.
+@end defvar
+
 @defvar Symbol.needs_frame
 This is @code{True} if evaluating this symbol's value requires a frame
 (@pxref{Frames In Python}) and @code{False} otherwise.  Typically,
diff --git a/gdb/python/py-symbol.c b/gdb/python/py-symbol.c
index 24b53bbe38a..38eead9cb3a 100644
--- a/gdb/python/py-symbol.c
+++ b/gdb/python/py-symbol.c
@@ -153,6 +153,16 @@ sympy_get_addr_class (PyObject *self, void *closure)
   return gdb_py_object_from_longest (symbol->aclass ()).release ();
 }
 
+static PyObject *
+sympy_get_domain (PyObject *self, void *closure)
+{
+  struct symbol *symbol = nullptr;
+
+  SYMPY_REQUIRE_VALID (self, symbol);
+
+  return gdb_py_object_from_longest (symbol->domain ()).release ();
+}
+
 static PyObject *
 sympy_is_argument (PyObject *self, void *closure)
 {
@@ -707,6 +717,7 @@ static gdb_PyGetSetDef symbol_object_getset[] = {
 This is either name or linkage_name, depending on whether the user asked GDB\n\
 to display demangled or mangled names.", NULL },
   { "addr_class", sympy_get_addr_class, NULL, "Address class of the symbol." },
+  { "domain", sympy_get_domain, nullptr, "Domain of the symbol." },
   { "is_argument", sympy_is_argument, NULL,
     "True if the symbol is an argument of a function." },
   { "is_constant", sympy_is_constant, NULL,
diff --git a/gdb/testsuite/gdb.python/py-symbol.exp b/gdb/testsuite/gdb.python/py-symbol.exp
index c174ba4c70e..2a74c97a137 100644
--- a/gdb/testsuite/gdb.python/py-symbol.exp
+++ b/gdb/testsuite/gdb.python/py-symbol.exp
@@ -135,6 +135,7 @@ gdb_test "python print (func.name)" "func" "test func.name"
 gdb_test "python print (func.print_name)" "func" "test func.print_name"
 gdb_test "python print (func.linkage_name)" "func" "test func.linkage_name"
 gdb_test "python print (func.addr_class == gdb.SYMBOL_LOC_BLOCK)" "True" "test func.addr_class"
+gdb_test "python print (func.domain == gdb.SYMBOL_FUNCTION_DOMAIN)" "True" "test func.domain"
 
 # Stop in a second file and ensure we find its local static symbol.
 gdb_breakpoint "function_in_other_file"
-- 
2.45.2


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [RFC v2 04/21] gdb/python: add void_type () method to gdb.Architecture object
  2024-11-21 12:46 [RFC v2 00/21] Add Python "JIT" API Jan Vrany
                   ` (2 preceding siblings ...)
  2024-11-21 12:46 ` [RFC v2 03/21] gdb/python: add domain property to gdb.Symbol Jan Vrany
@ 2024-11-21 12:46 ` Jan Vrany
  2024-11-21 13:32   ` Eli Zaretskii
  2024-11-21 12:46 ` [RFC v2 05/21] gdb/python: add function () method to gdb.Type object Jan Vrany
                   ` (16 subsequent siblings)
  20 siblings, 1 reply; 39+ messages in thread
From: Jan Vrany @ 2024-11-21 12:46 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany, Eli Zaretskii

This commit adds a new method to Python architecture objects that
returns a void type for that architecture.

This will be useful later to create types for function symbols created
using Python extension code.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
 gdb/NEWS                             |  3 +++
 gdb/doc/python.texi                  |  4 ++++
 gdb/python/py-arch.c                 | 16 ++++++++++++++++
 gdb/testsuite/gdb.python/py-arch.exp |  4 ++++
 4 files changed, 27 insertions(+)

diff --git a/gdb/NEWS b/gdb/NEWS
index 1ea9fcc65b9..765d14a1ae4 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -88,6 +88,9 @@
 
   ** Added gdb.Symbol.domain.  Contains the domain of the symbol.
 
+  ** Added gdb.Architecture.void_type. Returns a gdb.Type representing "void"
+     type for that architecture.
+
 * Debugger Adapter Protocol changes
 
   ** The "scopes" request will now return a scope holding global
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 09e374700a4..dac5115a5f8 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -7090,6 +7090,10 @@ If the indicated type cannot be found, this function will throw a
 @code{ValueError} exception.
 @end defun
 
+@defun Architecture.void_type ()
+This function returns a void type.
+@end defun
+
 @anchor{gdbpy_architecture_registers}
 @defun Architecture.registers (@r{[} reggroup @r{]})
 Return a @code{gdb.RegisterDescriptorIterator} (@pxref{Registers In
diff --git a/gdb/python/py-arch.c b/gdb/python/py-arch.c
index f7e35a4aa2b..6aed133eedd 100644
--- a/gdb/python/py-arch.c
+++ b/gdb/python/py-arch.c
@@ -318,6 +318,18 @@ archpy_integer_type (PyObject *self, PyObject *args, PyObject *kw)
   return type_to_type_object (type);
 }
 
+/* Implementation of gdb.void_type.  */
+static PyObject *
+archpy_void_type (PyObject *self, PyObject *args)
+{
+  struct gdbarch *gdbarch;
+  ARCHPY_REQUIRE_VALID (self, gdbarch);
+
+  builtin_type (gdbarch);
+
+  return type_to_type_object (builtin_type (gdbarch)->builtin_void);
+}
+
 /* __repr__ implementation for gdb.Architecture.  */
 
 static PyObject *
@@ -383,6 +395,10 @@ END_PC." },
     "integer_type (size [, signed]) -> type\n\
 Return an integer Type corresponding to the given bitsize and signed-ness.\n\
 If not specified, the type defaults to signed." },
+  { "void_type", (PyCFunction) archpy_void_type,
+    METH_NOARGS,
+    "void_type () -> type\n\
+Return an void Type." },
   { "registers", (PyCFunction) archpy_registers,
     METH_VARARGS | METH_KEYWORDS,
     "registers ([ group-name ]) -> Iterator.\n\
diff --git a/gdb/testsuite/gdb.python/py-arch.exp b/gdb/testsuite/gdb.python/py-arch.exp
index 14802ec80a3..c76fc778117 100644
--- a/gdb/testsuite/gdb.python/py-arch.exp
+++ b/gdb/testsuite/gdb.python/py-arch.exp
@@ -104,6 +104,10 @@ foreach_with_prefix test_data { {None None} \
 	"check 'signed' argument can handle non-bool type $bad_type"
 }
 
+gdb_test "python print(arch.void_type())" \
+    "void" \
+    "get void type"
+
 # Test for gdb.architecture_names().  First we're going to grab the
 # complete list of architecture names using the 'complete' command.
 set arch_names []
-- 
2.45.2


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [RFC v2 05/21] gdb/python: add function () method to gdb.Type object
  2024-11-21 12:46 [RFC v2 00/21] Add Python "JIT" API Jan Vrany
                   ` (3 preceding siblings ...)
  2024-11-21 12:46 ` [RFC v2 04/21] gdb/python: add void_type () method to gdb.Architecture object Jan Vrany
@ 2024-11-21 12:46 ` Jan Vrany
  2024-11-21 13:34   ` Eli Zaretskii
  2024-11-21 12:46 ` [RFC v2 06/21] gdb/python: add template function to implement equality comparison Jan Vrany
                   ` (15 subsequent siblings)
  20 siblings, 1 reply; 39+ messages in thread
From: Jan Vrany @ 2024-11-21 12:46 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany, Eli Zaretskii

This commit adds a new method to Python type objects that returns
possibly new function type returning that type. Parameter types can
be specified too.

This will be useful later to create types for function symbols created
using Python extension code.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
 gdb/NEWS                             |  3 ++
 gdb/doc/python.texi                  |  8 +++++
 gdb/python/py-type.c                 | 54 ++++++++++++++++++++++++++++
 gdb/testsuite/gdb.python/py-type.exp | 20 +++++++++++
 4 files changed, 85 insertions(+)

diff --git a/gdb/NEWS b/gdb/NEWS
index 765d14a1ae4..3d208744103 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -91,6 +91,9 @@
   ** Added gdb.Architecture.void_type. Returns a gdb.Type representing "void"
      type for that architecture.
 
+  ** Added gdb.Type.function.  Returns a new gdb.Type representing a function
+     returning that type.  Parameter types can be specified too.
+
 * Debugger Adapter Protocol changes
 
   ** The "scopes" request will now return a scope holding global
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index dac5115a5f8..272b51d32c5 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -1574,6 +1574,14 @@ Return a new @code{gdb.Type} object which represents a pointer to this
 type.
 @end defun
 
+@defun Type.function (@r{[}param_type...@r{]})
+Return a new @code{gdb.Type} object which represents a type of function
+returning this type.  @code{param_type...} arguments specify parameter
+types.  Use @code{None} as last parameter type to create a vararg function
+type.  When invoked with single @code{None} argument or with no arguments at
+all it creates a vararg function taking zero or more parameters.
+@end defun
+
 @defun Type.strip_typedefs ()
 Return a new @code{gdb.Type} that represents the real type,
 after removing all layers of typedefs.
diff --git a/gdb/python/py-type.c b/gdb/python/py-type.c
index 284960a3a87..348889dddd8 100644
--- a/gdb/python/py-type.c
+++ b/gdb/python/py-type.c
@@ -774,6 +774,57 @@ typy_unqualified (PyObject *self, PyObject *args)
   return type_to_type_object (type);
 }
 
+/* Return a function type. */
+static PyObject *
+typy_function (PyObject *self, PyObject *args)
+{
+  struct type *type = ((type_object *) self)->type;
+
+  gdb_assert (PySequence_Check (args));
+
+  std::vector<struct type *> param_types (PySequence_Length (args));
+
+  for (int i = 0; i < PySequence_Length (args); i++)
+    {
+      PyObject *param_type_obj = PySequence_GetItem (args, i);
+
+      if (param_type_obj == Py_None)
+	{
+	  param_types[i] = nullptr;
+	  if (i != (PySequence_Length (args) - 1))
+	    {
+	      PyErr_Format (PyExc_ValueError,
+			    _("Argument at index %d is None but None can "
+			      "only be the last type."), i);
+	      return nullptr;
+	    }
+	}
+      else
+	{
+	  param_types[i] = type_object_to_type (param_type_obj);
+	  if (!param_types[i])
+	    {
+	      PyErr_Format (PyExc_TypeError,
+			    _("Argument at index %d is not a gdb.Type "
+			      "object."), i);
+	      return nullptr;
+	    }
+	}
+    }
+
+  try
+    {
+      type = lookup_function_type_with_arguments (
+	       type, param_types.size (), param_types.data ());
+    }
+  catch (const gdb_exception &except)
+    {
+      return gdbpy_handle_gdb_exception (nullptr, except);
+    }
+
+  return type_to_type_object (type);
+}
+
 /* Return the size of the type represented by SELF, in bytes.  */
 static PyObject *
 typy_get_sizeof (PyObject *self, void *closure)
@@ -1641,6 +1692,9 @@ Return the type of a template argument." },
   { "unqualified", typy_unqualified, METH_NOARGS,
     "unqualified () -> Type\n\
 Return a variant of this type without const or volatile attributes." },
+  { "function", typy_function, METH_VARARGS,
+    "function () -> Type\n\
+Return a function type returning value of this type." },
   { "values", typy_values, METH_NOARGS,
     "values () -> list\n\
 Return a list holding all the fields of this type.\n\
diff --git a/gdb/testsuite/gdb.python/py-type.exp b/gdb/testsuite/gdb.python/py-type.exp
index 7e469c93c35..783261e6f4a 100644
--- a/gdb/testsuite/gdb.python/py-type.exp
+++ b/gdb/testsuite/gdb.python/py-type.exp
@@ -365,6 +365,26 @@ if { [build_inferior "${binfile}" "c"] == 0 } {
   gdb_test "python print(gdb.lookup_type('int').optimized_out())" \
       "<optimized out>"
 
+  gdb_test_no_output "python int_t = gdb.lookup_type('int')"
+
+  gdb_test "python print(repr(int_t.function()))" \
+      "<gdb.Type code=TYPE_CODE_FUNC name=int \\(\\)>"
+
+  gdb_test "python print(repr(int_t.function(int_t, int_t, int_t)))" \
+      "<gdb.Type code=TYPE_CODE_FUNC name=int \\(int, int, int\\)>"
+
+  gdb_test "python print(repr(int_t.function(int_t, None)))" \
+      "<gdb.Type code=TYPE_CODE_FUNC name=int \\(int, ...\\)>"
+
+  gdb_test "python print(repr(int_t.function(None)))" \
+      "<gdb.Type code=TYPE_CODE_FUNC name=int \\(\\)>"
+
+  gdb_test "python print(repr(int_t.function(123)))" \
+      "TypeError.*:.*"
+
+   gdb_test "python print(repr(int_t.function(int_t, None, int_t)))" \
+      "ValueError.*:.*"
+
   set sint [get_sizeof int 0]
   gdb_test "python print(gdb.parse_and_eval('aligncheck').type.alignof)" \
       $sint
-- 
2.45.2


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [RFC v2 06/21] gdb/python: add template function to implement equality comparison
  2024-11-21 12:46 [RFC v2 00/21] Add Python "JIT" API Jan Vrany
                   ` (4 preceding siblings ...)
  2024-11-21 12:46 ` [RFC v2 05/21] gdb/python: add function () method to gdb.Type object Jan Vrany
@ 2024-11-21 12:46 ` Jan Vrany
  2024-11-21 12:47 ` [RFC v2 07/21] gdb/python: make gdb.Symbol comparable for equality Jan Vrany
                   ` (14 subsequent siblings)
  20 siblings, 0 replies; 39+ messages in thread
From: Jan Vrany @ 2024-11-21 12:46 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany

This commit adds gdbpy_richcompare template to implement "default"
equality and non-equality comparison for GDB Python objects.

The "default" behavior is to consider two GDB Python objects as equal if
both refer to the same underlying GDB structure.
---
 gdb/python/python-internal.h | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index d723c4d577b..4d4810dc4cc 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -1145,4 +1145,37 @@ gdbpy_type_ready (PyTypeObject *type, PyObject *mod = nullptr)
 # define PyType_Ready POISONED_PyType_Ready
 #endif
 
+/* Implements default equality and non-equality comparisons for GDB
+   Python objects.
+
+   All other comparison operators will throw a TypeError Python exception.
+
+   Two Python objects of type P are considered equal  if both point to the
+   same underlying GBB structure of type D.  The last template parameter
+   specifies the member of Python object holding reference to underlying
+   GBB structure.  */
+
+template <typename P, typename D, D* P::*data>
+PyObject *
+gdbpy_richcompare (PyObject *self, PyObject *other, int op)
+{
+  int result;
+
+  if (!PyObject_TypeCheck (other, self->ob_type)
+      || (op != Py_EQ && op != Py_NE))
+    {
+      Py_INCREF (Py_NotImplemented);
+      return Py_NotImplemented;
+    }
+
+  if ( (P *)self->*data == (P *)other->*data)
+    result = Py_EQ;
+  else
+    result = Py_NE;
+
+  if (op == result)
+    Py_RETURN_TRUE;
+  Py_RETURN_FALSE;
+}
+
 #endif /* PYTHON_PYTHON_INTERNAL_H */
-- 
2.45.2


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [RFC v2 07/21] gdb/python: make gdb.Symbol comparable for equality
  2024-11-21 12:46 [RFC v2 00/21] Add Python "JIT" API Jan Vrany
                   ` (5 preceding siblings ...)
  2024-11-21 12:46 ` [RFC v2 06/21] gdb/python: add template function to implement equality comparison Jan Vrany
@ 2024-11-21 12:47 ` Jan Vrany
  2024-11-21 12:47 ` [RFC v2 08/21] gdb/python: make gdb.Symtab " Jan Vrany
                   ` (13 subsequent siblings)
  20 siblings, 0 replies; 39+ messages in thread
From: Jan Vrany @ 2024-11-21 12:47 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany

In subsequent patches, it will be useful to be able to compare two
gdb.Symbols. This patch makes gdb.Symbols equal iff both refer to the
same underlying struct symtab.
---
 gdb/python/py-symbol.c                 |  3 ++-
 gdb/testsuite/gdb.python/py-symbol.exp | 21 +++++++++++++++++++++
 2 files changed, 23 insertions(+), 1 deletion(-)

diff --git a/gdb/python/py-symbol.c b/gdb/python/py-symbol.c
index 38eead9cb3a..44bed85481b 100644
--- a/gdb/python/py-symbol.c
+++ b/gdb/python/py-symbol.c
@@ -767,7 +767,8 @@ PyTypeObject symbol_object_type = {
   "GDB symbol object",		  /*tp_doc */
   0,				  /*tp_traverse */
   0,				  /*tp_clear */
-  0,				  /*tp_richcompare */
+  gdbpy_richcompare<symbol_object, symbol, &symbol_object::symbol>,
+				    /*tp_richcompare */
   0,				  /*tp_weaklistoffset */
   0,				  /*tp_iter */
   0,				  /*tp_iternext */
diff --git a/gdb/testsuite/gdb.python/py-symbol.exp b/gdb/testsuite/gdb.python/py-symbol.exp
index 2a74c97a137..1bfa17b4e91 100644
--- a/gdb/testsuite/gdb.python/py-symbol.exp
+++ b/gdb/testsuite/gdb.python/py-symbol.exp
@@ -201,6 +201,27 @@ if { [is_remote host] } {
 }
 gdb_test "python print (t\[0\].symtab)" "${py_symbol_c}" "get symtab"
 
+# Test comparison for equality and non-equality
+gdb_test "python print (t\[0\] == t\[0\] )"\
+	 "True" \
+	 "test symbol equality with itself"
+gdb_test "python print (t\[0\] == a\[0\] )"\
+	 "False" \
+	 "test symbol equality with other symbol"
+gdb_test "python print (t\[0\] == 123 )"\
+	 "False" \
+	 "test symbol equality with non-symbol"
+
+gdb_test "python print (t\[0\] != t\[0\] )"\
+	 "False" \
+	 "test symbol non-equality with itself"
+gdb_test "python print (t\[0\] != a\[0\] )"\
+	 "True" \
+	 "test symbol non-equality with other symbol"
+gdb_test "python print (t\[0\] != 123 )"\
+	 "True" \
+	 "test symbol non-equality with non-symbol"
+
 # C++ tests
 # Recompile binary.
 lappend opts c++
-- 
2.45.2


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [RFC v2 08/21] gdb/python: make gdb.Symtab comparable for equality
  2024-11-21 12:46 [RFC v2 00/21] Add Python "JIT" API Jan Vrany
                   ` (6 preceding siblings ...)
  2024-11-21 12:47 ` [RFC v2 07/21] gdb/python: make gdb.Symbol comparable for equality Jan Vrany
@ 2024-11-21 12:47 ` Jan Vrany
  2024-11-21 12:47 ` [RFC v2 09/21] gdb: use std::vector<> to hold on blocks in struct blockvector Jan Vrany
                   ` (12 subsequent siblings)
  20 siblings, 0 replies; 39+ messages in thread
From: Jan Vrany @ 2024-11-21 12:47 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany

Like previous patch, but for gdb.Symtab.
---
 gdb/python/py-symtab.c                 |  3 ++-
 gdb/testsuite/gdb.python/py-symtab.exp | 21 +++++++++++++++++++++
 2 files changed, 23 insertions(+), 1 deletion(-)

diff --git a/gdb/python/py-symtab.c b/gdb/python/py-symtab.c
index 99a5094ba60..5330b15ef8d 100644
--- a/gdb/python/py-symtab.c
+++ b/gdb/python/py-symtab.c
@@ -579,7 +579,8 @@ PyTypeObject symtab_object_type = {
   "GDB symtab object",		  /*tp_doc */
   0,				  /*tp_traverse */
   0,				  /*tp_clear */
-  0,				  /*tp_richcompare */
+  gdbpy_richcompare<symtab_object, symtab, &symtab_object::symtab>,
+				    /*tp_richcompare */
   0,				  /*tp_weaklistoffset */
   0,				  /*tp_iter */
   0,				  /*tp_iternext */
diff --git a/gdb/testsuite/gdb.python/py-symtab.exp b/gdb/testsuite/gdb.python/py-symtab.exp
index 4765ef5cb2f..9caa5f1ebad 100644
--- a/gdb/testsuite/gdb.python/py-symtab.exp
+++ b/gdb/testsuite/gdb.python/py-symtab.exp
@@ -89,6 +89,27 @@ gdb_test_multiple "python print (\"simple_struct\" in static_symbols)" \
 	    }
 	}
     }
+# Test comparison for equality and non-equality
+gdb_test "python print (symtab == symtab)"\
+    "True" \
+    "test symtab equality with itself"
+gdb_test "python print (symtab == sal.symtab)"\
+    "True" \
+    "test symtab equality with other symtab object referring to the same symtab"
+gdb_test "python print (symtab == 123 )"\
+    "False" \
+    "test symtab equality with non-symtab"
+
+gdb_test "python print (symtab != symtab)"\
+    "False" \
+    "test symtab non-equality with itself"
+gdb_test "python print (symtab != sal.symtab)"\
+    "False" \
+    "test symtab non-equality with other symtab object referring to the same symtab"
+gdb_test "python print (symtab != 123 )"\
+    "True" \
+    "test symtab non-equality with non-symtab"
+
 
 # Test is_valid when the objfile is unloaded.  This must be the last
 # test as it unloads the object file in GDB.
-- 
2.45.2


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [RFC v2 09/21] gdb: use std::vector<> to hold on blocks in struct blockvector
  2024-11-21 12:46 [RFC v2 00/21] Add Python "JIT" API Jan Vrany
                   ` (7 preceding siblings ...)
  2024-11-21 12:47 ` [RFC v2 08/21] gdb/python: make gdb.Symtab " Jan Vrany
@ 2024-11-21 12:47 ` Jan Vrany
  2024-11-21 12:47 ` [RFC v2 10/21] gdb/python: add gdb.Compunit Jan Vrany
                   ` (11 subsequent siblings)
  20 siblings, 0 replies; 39+ messages in thread
From: Jan Vrany @ 2024-11-21 12:47 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany

This commit changes internal implementation of struct blockvector to use
std::vector<> rather than flexible array.

The main motivation for this change is to simplify adding blocks to
existing symtab. This feature will be used later by Python API to build
objfiles, compunits and symtabs dynamically (similarly to JIT reader
API).

To do so, this commit

 1. introduces obstack_allocator, an implementation of Allocator
    concept that allocates memory on obstack.
 2. uses std::vector<> with the above allocator to hold on blocks
 3. updates users.

As a side-effect of this change, blockvectors allocated in mdebugread.c
are obstack-allocated rather than xzalloc()ated which seems to be the
correct thing to do. Also, code got simpler.

The downside is higher memory consumption. The size of std::vector with
obstack allocator is 32 bytes (GCC 14) compared to 8 bytes used
currently to store the number of blocks (m_num_blocks). Stopping gdb at
its main(), followed by "maint expand-symtabs" results in 4593
compunit symtabs so in this case the overhead is 24*4593 = 110232 bytes
which I hope is acceptable.

Maybe more concerning is the fact that one may waste obstack memory when
excessively adding blocks. However, blockvectors are not added blocks
after initial allocation at the moment (except in mdebugread.c) so this
is not a problem for existing code. To to mitigate this issue code
allocating may capacity - a number of blocks the blockvector may hold
without reallocating.
---
 gdb/block.c              | 59 ++++++++++++++++++++++++++++++++++++++++
 gdb/block.h              | 56 +++++++++++++++++++++++++++-----------
 gdb/buildsym.c           |  6 +---
 gdb/jit.c                |  8 +-----
 gdb/mdebugread.c         | 32 ++--------------------
 gdbsupport/gdb_obstack.h | 52 +++++++++++++++++++++++++++++++++++
 6 files changed, 156 insertions(+), 57 deletions(-)

diff --git a/gdb/block.c b/gdb/block.c
index cf7ca6785b0..1162235aa07 100644
--- a/gdb/block.c
+++ b/gdb/block.c
@@ -17,6 +17,7 @@
    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 <cstring>
 #include "block.h"
 #include "symtab.h"
 #include "symfile.h"
@@ -821,6 +822,64 @@ make_blockranges (struct objfile *objfile,
   return blr;
 }
 
+static bool
+block_ordering_predicate(struct block *b1, struct block *b2)
+{
+  CORE_ADDR start1 = b1->start ();
+  CORE_ADDR start2 = b2->start ();
+
+  if (start1 != start2)
+    return start1 < start2;
+  return (b2->end () < b1->end ());
+}
+
+/* See block.h.  */
+
+void
+blockvector::add_block (struct block *block)
+{
+  if (num_blocks() <= FIRST_LOCAL_BLOCK)
+    {
+      /* No blocks (except global and static block).  */
+      m_blocks.push_back (block);
+    }
+  else
+    {
+      /* blockvector already contains some blocks.  Insert new block
+	 to a correct place.  */
+      auto first = m_blocks.begin () + FIRST_LOCAL_BLOCK;
+      auto last = m_blocks.end ();
+
+      auto insert_before = std::upper_bound (first,
+					     last,
+					     block,
+					     block_ordering_predicate);
+
+      m_blocks.insert (insert_before, block);
+    }
+}
+
+/* See block.h.  */
+
+void
+blockvector::sort ()
+{
+  if (num_blocks() > FIRST_LOCAL_BLOCK)
+    {
+      std::sort (blocks ().begin () + FIRST_LOCAL_BLOCK,
+		 blocks ().end (),
+		 block_ordering_predicate);
+    }
+}
+
+/* See block.h.  */
+
+struct blockvector *
+allocate_blockvector(struct obstack *obstack, int nblocks, int capacity)
+{
+  return new (obstack) blockvector(obstack, nblocks, capacity);
+}
+
 /* Implement 'maint info blocks' command.  If passed an argument then
    print a list of all blocks at the given address.  With no arguments
    then list all blocks at the current address of the current inferior.  */
diff --git a/gdb/block.h b/gdb/block.h
index 05daa734642..99b424c23aa 100644
--- a/gdb/block.h
+++ b/gdb/block.h
@@ -20,6 +20,7 @@
 #ifndef BLOCK_H
 #define BLOCK_H
 
+#include <algorithm>
 #include "dictionary.h"
 #include "gdbsupport/array-view.h"
 #include "gdbsupport/next-iterator.h"
@@ -416,41 +417,60 @@ struct global_block : public block
 
 struct blockvector
 {
+  void* operator new (size_t size, struct obstack *obstack)
+  {
+    return obstack_alloc (obstack, size);
+  }
+
+  void* operator new[] (size_t size, struct obstack *obstack)
+  {
+    return obstack_alloc (obstack, size);
+  }
+
+  void operator delete (void *memory) {}
+  void operator delete[] (void *memory) {}
+
+  blockvector (struct obstack *obstack, int nblocks, int capacity = 0)
+    : m_map (nullptr),
+      m_blocks (0, nullptr, obstack_allocator<struct block *> (obstack))
+  {
+    m_blocks.reserve (std::max (nblocks, capacity));
+    m_blocks.resize (nblocks, nullptr);
+  }
+
   /* Return a view on the blocks of this blockvector.  */
   gdb::array_view<struct block *> blocks ()
   {
-    return gdb::array_view<struct block *> (m_blocks, m_num_blocks);
+    return gdb::array_view<struct block *> (m_blocks.data (), m_blocks.size ());
   }
 
   /* Const version of the above.  */
   gdb::array_view<const struct block *const> blocks () const
   {
-    const struct block **blocks = (const struct block **) m_blocks;
-    return gdb::array_view<const struct block *const> (blocks, m_num_blocks);
+    const struct block **blocks = (const struct block **) m_blocks.data ();
+    return gdb::array_view<const struct block *const> (blocks, m_blocks.size ());
   }
 
   /* Return the block at index I.  */
   struct block *block (size_t i)
-  { return this->blocks ()[i]; }
+  { return m_blocks[i]; }
 
   /* Const version of the above.  */
   const struct block *block (size_t i) const
-  { return this->blocks ()[i]; }
+  { return m_blocks[i]; }
 
   /* Set the block at index I.  */
   void set_block (int i, struct block *block)
   { m_blocks[i] = block; }
 
-  /* Set the number of blocks of this blockvector.
-
-     The storage of blocks is done using a flexible array member, so the number
-     of blocks set here must agree with what was effectively allocated.  */
-  void set_num_blocks (int num_blocks)
-  { m_num_blocks = num_blocks; }
+  /* Add BLOCK, making sure blocks are ordered by code-addresses
+     as required. Update global and static block start and end
+     adresses accordingly.  */
+  void add_block(struct block *block);
 
   /* Return the number of blocks in this blockvector.  */
   int num_blocks () const
-  { return m_num_blocks; }
+  { return m_blocks.size (); }
 
   /* Return the global block of this blockvector.  */
   struct global_block *global_block ()
@@ -483,19 +503,23 @@ struct blockvector
   void set_map (addrmap_fixed *map)
   { m_map = map; }
 
+  void sort ();
+
 private:
   /* An address map mapping addresses to blocks in this blockvector.
      This pointer is zero if the blocks' start and end addresses are
      enough.  */
   addrmap_fixed *m_map;
 
-  /* Number of blocks in the list.  */
-  int m_num_blocks;
-
   /* The blocks themselves.  */
-  struct block *m_blocks[1];
+  std::vector<struct block *, obstack_allocator<struct block *>> m_blocks;
 };
 
+/* Allocate new blockvector for NBLOCKS blocks with enough storage to
+   hold up to CAPACITY blocks.  CAPACITY defaults to NBLOCKS.  */
+struct blockvector *allocate_blockvector(struct obstack *obstack,
+					 int nblocks, int capacity = 0);
+
 extern const struct blockvector *blockvector_for_pc (CORE_ADDR,
 					       const struct block **);
 
diff --git a/gdb/buildsym.c b/gdb/buildsym.c
index 02d6848f3a4..35941ad63e5 100644
--- a/gdb/buildsym.c
+++ b/gdb/buildsym.c
@@ -429,10 +429,7 @@ buildsym_compunit::make_blockvector ()
     {
     }
 
-  blockvector = (struct blockvector *)
-    obstack_alloc (&m_objfile->objfile_obstack,
-		   (sizeof (struct blockvector)
-		    + (i - 1) * sizeof (struct block *)));
+  blockvector = allocate_blockvector(&m_objfile->objfile_obstack, i);
 
   /* Copy the blocks into the blockvector.  This is done in reverse
      order, which happens to put the blocks into the proper order
@@ -440,7 +437,6 @@ buildsym_compunit::make_blockvector ()
      each block into the list after its subblocks in order to make
      sure this is true.  */
 
-  blockvector->set_num_blocks (i);
   for (next = m_pending_blocks; next; next = next->next)
     blockvector->set_block (--i, next->block);
 
diff --git a/gdb/jit.c b/gdb/jit.c
index 48be1c880c9..856929c6d64 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -517,7 +517,6 @@ static void
 finalize_symtab (struct gdb_symtab *stab, struct objfile *objfile)
 {
   struct compunit_symtab *cust;
-  size_t blockvector_size;
   CORE_ADDR begin, end;
   struct blockvector *bv;
 
@@ -552,18 +551,13 @@ finalize_symtab (struct gdb_symtab *stab, struct objfile *objfile)
       filetab->set_linetable (new_table);
     }
 
-  blockvector_size = (sizeof (struct blockvector)
-		      + (actual_nblocks - 1) * sizeof (struct block *));
-  bv = (struct blockvector *) obstack_alloc (&objfile->objfile_obstack,
-					     blockvector_size);
+  bv = allocate_blockvector(&objfile->objfile_obstack, actual_nblocks);
   cust->set_blockvector (bv);
 
   /* At the end of this function, (begin, end) will contain the PC range this
      entire blockvector spans.  */
-  bv->set_map (nullptr);
   begin = stab->blocks.front ().begin;
   end = stab->blocks.front ().end;
-  bv->set_num_blocks (actual_nblocks);
 
   /* First run over all the gdb_block objects, creating a real block
      object for each.  Simultaneously, keep setting the real_block
diff --git a/gdb/mdebugread.c b/gdb/mdebugread.c
index dae4e4d2854..ea88faae90b 100644
--- a/gdb/mdebugread.c
+++ b/gdb/mdebugread.c
@@ -242,8 +242,6 @@ static struct compunit_symtab *new_symtab (const char *, int, struct objfile *);
 
 static struct linetable *new_linetable (int);
 
-static struct blockvector *new_bvect (int);
-
 static struct type *parse_type (int, union aux_ext *, unsigned int, int *,
 				int, const char *);
 
@@ -4499,17 +4497,8 @@ add_block (struct block *b, struct symtab *s)
   /* Cast away "const", but that's ok because we're building the
      symtab and blockvector here.  */
   struct blockvector *bv
-    = (struct blockvector *) s->compunit ()->blockvector ();
-
-  bv = (struct blockvector *) xrealloc ((void *) bv,
-					(sizeof (struct blockvector)
-					 + bv->num_blocks ()
-					 * sizeof (struct block)));
-  if (bv != s->compunit ()->blockvector ())
-    s->compunit ()->set_blockvector (bv);
-
-  bv->set_block (bv->num_blocks (), b);
-  bv->set_num_blocks (bv->num_blocks () + 1);
+    = const_cast<struct blockvector*> (s->compunit ()->blockvector ());
+  bv->add_block (b);
 }
 
 /* Add a new linenumber entry (LINENO,ADR) to a linevector LT.
@@ -4632,7 +4621,7 @@ new_symtab (const char *name, int maxlines, struct objfile *objfile)
   lang = cust->language ();
 
   /* All symtabs must have at least two blocks.  */
-  bv = new_bvect (2);
+  bv = allocate_blockvector(&objfile->objfile_obstack, 2);
   bv->set_block (GLOBAL_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK, lang));
   bv->set_block (STATIC_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK, lang));
   bv->static_block ()->set_superblock (bv->global_block ());
@@ -4700,21 +4689,6 @@ shrink_linetable (struct linetable *lt)
 					    * sizeof (lt->item))));
 }
 
-/* Allocate and zero a new blockvector of NBLOCKS blocks.  */
-
-static struct blockvector *
-new_bvect (int nblocks)
-{
-  struct blockvector *bv;
-  int size;
-
-  size = sizeof (struct blockvector) + nblocks * sizeof (struct block *);
-  bv = (struct blockvector *) xzalloc (size);
-  bv->set_num_blocks (nblocks);
-
-  return bv;
-}
-
 /* Allocate and zero a new block of language LANGUAGE, and set its
    BLOCK_MULTIDICT.  If function is non-zero, assume the block is
    associated to a function, and make sure that the symbols are stored
diff --git a/gdbsupport/gdb_obstack.h b/gdbsupport/gdb_obstack.h
index 7b3bb05bc00..a5a6dacf963 100644
--- a/gdbsupport/gdb_obstack.h
+++ b/gdbsupport/gdb_obstack.h
@@ -20,6 +20,8 @@
 #if !defined (GDB_OBSTACK_H)
 #define GDB_OBSTACK_H 1
 
+#include <limits>
+#include <type_traits>
 #include "obstack.h"
 
 /* Utility macros - wrap obstack alloc into something more robust.  */
@@ -157,4 +159,54 @@ struct allocate_on_obstack
   void operator delete[] (void *memory) {}
 };
 
+/* Implementation of Allocator concept using obstack to
+   allocate memory. This allows standard containers to be
+   used with obstack.  */
+
+template <typename T>
+class obstack_allocator
+{
+public:
+  typedef T value_type;
+
+  obstack_allocator (struct obstack *obstack)
+    : m_obstack(obstack)
+    {}
+
+  template <typename U> constexpr obstack_allocator (const obstack_allocator<U>& allocator) noexcept
+    : m_obstack(allocator.m_obstack)
+  {}
+
+  T* allocate (std::size_t n)
+  {
+    if (n > std::numeric_limits<std::size_t>::max () / sizeof (T))
+      throw std::bad_array_new_length ();
+
+    if (auto p = static_cast<T*> (obstack_alloc (m_obstack, n * sizeof (T))))
+      {
+	return p;
+      }
+
+    throw std::bad_alloc ();
+  }
+
+  void deallocate(T* p, std::size_t n) noexcept
+  {}
+
+private:
+
+  struct obstack *m_obstack;
+};
+
+template <class T, class U>
+bool operator==(const obstack_allocator<T> &t, const obstack_allocator<U> &u)
+{
+  return (std::is_same<T, U>::value_type) && (t.m_obstack == u.m_obstack);
+}
+template <class T, class U>
+bool operator!=(const obstack_allocator<T> &t, const obstack_allocator<U> &u)
+{
+  return ! (t == u);
+}
+
 #endif
-- 
2.45.2


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [RFC v2 10/21] gdb/python: add gdb.Compunit
  2024-11-21 12:46 [RFC v2 00/21] Add Python "JIT" API Jan Vrany
                   ` (8 preceding siblings ...)
  2024-11-21 12:47 ` [RFC v2 09/21] gdb: use std::vector<> to hold on blocks in struct blockvector Jan Vrany
@ 2024-11-21 12:47 ` Jan Vrany
  2024-11-21 13:39   ` Eli Zaretskii
  2024-11-21 12:47 ` [RFC v2 11/21] gdb/python: allow instantiation of gdb.Objfile from Python Jan Vrany
                   ` (10 subsequent siblings)
  20 siblings, 1 reply; 39+ messages in thread
From: Jan Vrany @ 2024-11-21 12:47 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany, Eli Zaretskii

This commit introduces gdb.Compunit - a representation of struct
compunit_symtab in Python.

It also adds method gdb.Objfile.compunits() to get a list of compunits
for an objfile and adds compunit attribute to gdb.Block and gdb.Symtab
to access compunit containing given block or symbol table.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
 gdb/Makefile.in                          |   1 +
 gdb/NEWS                                 |   2 +
 gdb/doc/python.texi                      |  69 +++++
 gdb/python/py-block.c                    |  15 ++
 gdb/python/py-compunit.c                 | 319 +++++++++++++++++++++++
 gdb/python/py-objfile.c                  |  29 +++
 gdb/python/py-symtab.c                   |  14 +
 gdb/python/python-internal.h             |   3 +
 gdb/testsuite/gdb.python/py-block.exp    |   2 +
 gdb/testsuite/gdb.python/py-compunit.exp |  62 +++++
 gdb/testsuite/gdb.python/py-symtab.exp   |   8 +
 11 files changed, 524 insertions(+)
 create mode 100644 gdb/python/py-compunit.c
 create mode 100644 gdb/testsuite/gdb.python/py-compunit.exp

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index ecb323d8f02..88566b1d41a 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -438,6 +438,7 @@ SUBDIR_PYTHON_SRCS = \
 	python/py-value.c \
 	python/py-varobj.c \
 	python/py-xmethods.c \
+	python/py-compunit.c \
 	python/python.c
 
 SUBDIR_PYTHON_OBS = $(patsubst %.c,%.o,$(SUBDIR_PYTHON_SRCS))
diff --git a/gdb/NEWS b/gdb/NEWS
index 3d208744103..7273b23f989 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -94,6 +94,8 @@
   ** Added gdb.Type.function.  Returns a new gdb.Type representing a function
      returning that type.  Parameter types can be specified too.
 
+  ** Added class gdb.Compunit.
+
 * Debugger Adapter Protocol changes
 
   ** The "scopes" request will now return a scope holding global
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 272b51d32c5..2065f001320 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -220,6 +220,7 @@ optional arguments while skipping others.  Example:
 * Blocks In Python::            Accessing blocks from Python.
 * Symbols In Python::           Python representation of symbols.
 * Symbol Tables In Python::     Python representation of symbol tables.
+* Compunits In Python::         Python representation of compunits.
 * Line Tables In Python::       Python representation of line tables.
 * Breakpoints In Python::       Manipulating breakpoints using Python.
 * Finish Breakpoints in Python:: Setting Breakpoints on function return
@@ -5691,6 +5692,11 @@ Like @code{Objfile.lookup_global_symbol}, but searches for a global
 symbol with static linkage named @var{name} in this objfile.
 @end defun
 
+@defun Objfile.compunits ()
+Return a sequence of all the compunits associated with this objfile.
+@xref{Compunits In Python}.
+@end defun
+
 @node Frames In Python
 @subsubsection Accessing inferior stack frames from Python
 
@@ -6043,6 +6049,11 @@ have a superblock that is not the static block -- for instance this
 happens for an inlined function.
 @end defvar
 
+@defvar Block.compunit
+The compunit containing this block.  @xref{Compunits In Python}.
+This attribute is not writable.
+@end defvar
+
 @defvar Block.superblock
 The block containing this block.  If this parent block does not exist,
 this attribute holds @code{None}.  This attribute is not writable.
@@ -6471,6 +6482,11 @@ If no producer information is available then @code{None} is returned.
 This attribute is not writable.
 @end defvar
 
+@defvar Symtab.compunit
+The compunit this symbol table belongs to.  @xref{Compunits In Python}.
+This attribute is not writable.
+@end defvar
+
 A @code{gdb.Symtab} object has the following methods:
 
 @defun Symtab.is_valid ()
@@ -6500,6 +6516,59 @@ Return the line table associated with the symbol table.
 @xref{Line Tables In Python}.
 @end defun
 
+@node Compunits In Python
+@subsubsection Compunits representation in Python
+
+@cindex compunits in python
+@tindex gdb.Compunit
+
+Access to compunits maintained by @value{GDBN} on objfiles
+is exposed to Python via @code{gdb.Compunit}.  Compunit for a symbol table can
+be accessed via @code{compunit} property of @code{gdb.Symtab} object.
+@xref{Symbol Tables In Python}.  Method @code{compunits} of
+@code{gdb.Objfile} can be used to get a list of all compunits belonging to
+that objfile.  @xref{Objfiles In Python}.
+
+A @code{gdb.Compunit} object has the following attributes:
+
+@defvar Compunit.objfile
+The compunits' backing object file.  @xref{Objfiles In Python}.
+This attribute is not writable.
+@end defvar
+
+@defvar Compunit.producer
+The name and possibly version number of the program that
+compiled the code in the compunit.
+The contents of this string is up to the compiler.
+If no producer information is available then @code{None} is returned.
+This attribute is not writable.
+@end defvar
+
+@defvar Compunit.symtabs
+The list of symbol tables associated with this compunit.
+@xref{Symbol Tables In Python}.  This attribute is not writable.
+@end defvar
+
+A @code{gdb.Compunit} object has the following methods:
+
+@defun Compunit.is_valid ()
+Returns @code{True} if the @code{gdb.Compunit} object is valid,
+@code{False} if not.  A @code{gdb.Compunit} object can become invalid if
+the compunit it refers to does not exist in @value{GDBN} any
+longer.  All other @code{gdb.Compunit} methods will throw an exception
+if it is invalid at the time the method is called.
+@end defun
+
+@defun Compunit.global_block ()
+Return the global block of the underlying compunit.
+@xref{Blocks In Python}.
+@end defun
+
+@defun Compunit.static_block ()
+Return the static block of the underlying compunit.
+@xref{Blocks In Python}.
+@end defun
+
 @node Line Tables In Python
 @subsubsection Manipulating line tables using Python
 
diff --git a/gdb/python/py-block.c b/gdb/python/py-block.c
index 1c5eab44b3a..a05a2796c52 100644
--- a/gdb/python/py-block.c
+++ b/gdb/python/py-block.c
@@ -215,6 +215,19 @@ blpy_get_static_block (PyObject *self, void *closure)
   return block_to_block_object (static_block, self_obj->objfile);
 }
 
+/* Getter function for Block.compunit.  */
+
+static PyObject *
+blpy_get_compunit (PyObject *self, void *closure)
+{
+  const struct block *block;
+
+  BLPY_REQUIRE_VALID (self, block);
+
+  return compunit_to_compunit_object (
+	   block->global_block ()->compunit ()).release ();
+}
+
 /* Implementation of gdb.Block.is_global (self) -> Boolean.
    Returns True if this block object is a global block.  */
 
@@ -553,6 +566,8 @@ static gdb_PyGetSetDef block_object_getset[] = {
     "Block containing the global block.", NULL },
   { "static_block", blpy_get_static_block, NULL,
     "Block containing the static block.", NULL },
+  { "compunit", blpy_get_compunit, nullptr,
+    "Compunit containing this block.", nullptr },
   { "is_static", blpy_is_static, NULL,
     "Whether this block is a static block.", NULL },
   { "is_global", blpy_is_global, NULL,
diff --git a/gdb/python/py-compunit.c b/gdb/python/py-compunit.c
new file mode 100644
index 00000000000..829746cc92d
--- /dev/null
+++ b/gdb/python/py-compunit.c
@@ -0,0 +1,319 @@
+/* Python interface to compunits.
+
+   Copyright (C) 2008-2024 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 "charset.h"
+#include "symtab.h"
+#include "source.h"
+#include "python-internal.h"
+#include "objfiles.h"
+#include "block.h"
+
+struct compunit_object {
+  PyObject_HEAD
+
+  /* The GDB compunit structure.  */
+  struct compunit_symtab *compunit;
+
+  /* A compunit object is associated with an objfile, so keep track with
+     a doubly-linked list, rooted in the objfile.  This allows
+     invalidation of the underlying struct compunit_symtab when the objfile is
+     deleted.  */
+  compunit_object *prev;
+  compunit_object *next;
+};
+
+/* This function is called when an objfile is about to be freed.
+   Invalidate the compunit as further actions on the compunit
+   would result in bad data.  All access to obj->compunit should be
+   gated by CUPY_REQUIRE_VALID which will raise an exception on
+   compunits.  */
+struct cupy_deleter
+{
+  void operator() (compunit_object *obj)
+  {
+    while (obj)
+      {
+	compunit_object *next = obj->next;
+
+	obj->compunit = nullptr;
+	obj->next = nullptr;
+	obj->prev = nullptr;
+	obj = next;
+      }
+  }
+};
+
+extern PyTypeObject compunit_object_type
+    CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("compunit_object");
+static const registry<objfile>::key<compunit_object, cupy_deleter>
+     cupy_objfile_data_key;
+
+/* Require a valid compunit.  All access to compunit_object->compunit
+   should be gated by this call.  */
+
+#define CUPY_REQUIRE_VALID(compunit_obj, compunit)		 \
+  do {							 \
+    compunit = compunit_object_to_compunit (compunit_obj);	 \
+    if (compunit == nullptr)					 \
+      {							 \
+	PyErr_SetString (PyExc_RuntimeError,		 \
+			 _("Compunit object is invalid.")); \
+	return nullptr;					 \
+      }							 \
+  } while (0)
+
+
+/* Getter function for gdb.Compunit.objfile.  */
+
+static PyObject *
+cupy_get_objfile (PyObject *self, void *closure)
+{
+  struct compunit_symtab *compunit = nullptr;
+
+  CUPY_REQUIRE_VALID (self, compunit);
+
+  return objfile_to_objfile_object (compunit->objfile ()).release ();
+}
+
+/* Getter function for gdb.Compunit.producer.  */
+
+static PyObject *
+cupy_get_producer (PyObject *self, void *closure)
+{
+  struct compunit_symtab *compunit = nullptr;
+
+  CUPY_REQUIRE_VALID (self, compunit);
+  if (compunit->producer () != nullptr)
+    {
+      const char *producer = compunit->producer ();
+
+      return host_string_to_python_string (producer).release ();
+    }
+
+  Py_RETURN_NONE;
+}
+
+/* Implementation of gdb.Compunit.is_valid (self) -> Boolean.
+   Returns True if this Symbol table still exists in GDB.  */
+
+static PyObject *
+cupy_is_valid (PyObject *self, PyObject *args)
+{
+  struct compunit_symtab *compunit = nullptr;
+
+  compunit = compunit_object_to_compunit (self);
+  if (compunit == nullptr)
+    Py_RETURN_FALSE;
+
+  Py_RETURN_TRUE;
+}
+
+/* Return the GLOBAL_BLOCK of the underlying compunit.  */
+
+static PyObject *
+cupy_global_block (PyObject *self, PyObject *args)
+{
+  struct compunit_symtab *compunit = nullptr;
+  const struct blockvector *blockvector;
+
+  CUPY_REQUIRE_VALID (self, compunit);
+
+  blockvector = compunit->blockvector ();
+  const struct block *block = blockvector->global_block ();
+
+  return block_to_block_object (block, compunit->objfile ());
+}
+
+/* Return the STATIC_BLOCK of the underlying compunit.  */
+
+static PyObject *
+cupy_static_block (PyObject *self, PyObject *args)
+{
+  struct compunit_symtab *compunit = nullptr;
+  const struct blockvector *blockvector;
+
+  CUPY_REQUIRE_VALID (self, compunit);
+
+  blockvector = compunit->blockvector ();
+  const struct block *block = blockvector->static_block ();
+
+  return block_to_block_object (block, compunit->objfile ());
+}
+
+static PyObject *
+cupy_get_symtabs (PyObject *self, void *closure)
+{
+  struct compunit_symtab *compunit = nullptr;
+
+  CUPY_REQUIRE_VALID (self, compunit);
+
+  gdbpy_ref<> list (PyList_New (0));
+  if (list == nullptr)
+    return nullptr;
+
+  for (struct symtab *each : compunit->filetabs ())
+    {
+      {
+	gdbpy_ref<> item (symtab_to_symtab_object (each));
+	if (item.get () == nullptr
+	      || PyList_Append (list.get (), item.get ()) == -1)
+	    return nullptr;
+	}
+    }
+
+  return list.release ();
+}
+
+static void
+cupy_dealloc (PyObject *obj)
+{
+  compunit_object *compunit = (compunit_object *) obj;
+
+  if (compunit->prev)
+    compunit->prev->next = compunit->next;
+  else if (compunit->compunit)
+    cupy_objfile_data_key.set (compunit->compunit->objfile (),
+			       compunit->next);
+  if (compunit->next)
+    compunit->next->prev = compunit->prev;
+  compunit->compunit = nullptr;
+  Py_TYPE (obj)->tp_free (obj);
+}
+
+/* Given a compunit, and a compunit_object that has previously been
+   allocated and initialized, populate the compunit_object with the
+   struct compunit_symtab data.  Also, register the compunit_object life-cycle
+   with the life-cycle of the object file associated with this
+   compunit, if needed.  */
+static void
+set_compunit (compunit_object *obj, struct compunit_symtab *compunit)
+{
+  obj->compunit = compunit;
+  obj->prev = nullptr;
+  if (compunit)
+    {
+      obj->next = cupy_objfile_data_key.get (compunit->objfile ());
+      if (obj->next)
+	obj->next->prev = obj;
+      cupy_objfile_data_key.set (compunit->objfile (), obj);
+    }
+  else
+    obj->next = nullptr;
+}
+
+/* Return a new reference to gdb.Compunit Python object representing
+   COMPUNIT.  Return NULL and set the Python error on failure.  */
+gdbpy_ref<>
+compunit_to_compunit_object (struct compunit_symtab *compunit)
+{
+  compunit_object *compunit_obj;
+
+  compunit_obj = PyObject_NEW(compunit_object, &compunit_object_type);
+  if (compunit_obj)
+    set_compunit(compunit_obj, compunit);
+
+  return gdbpy_ref<>::new_reference ((PyObject * )compunit_obj);
+}
+
+/* Return struct compunit_symtab reference that is wrapped by this object.  */
+struct compunit_symtab *
+compunit_object_to_compunit (PyObject *obj)
+{
+  if (! PyObject_TypeCheck (obj, &compunit_object_type))
+    return nullptr;
+  return ((compunit_object *) obj)->compunit;
+}
+
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
+gdbpy_initialize_compunits (void)
+{
+  if (gdbpy_type_ready (&compunit_object_type) < 0)
+    return -1;
+
+  return 0;
+}
+
+GDBPY_INITIALIZE_FILE (gdbpy_initialize_compunits);
+
+\f
+
+static gdb_PyGetSetDef compunit_object_getset[] = {
+  { "objfile", cupy_get_objfile, nullptr, "The compunit's objfile.",
+    nullptr },
+  { "producer", cupy_get_producer, nullptr,
+    "The name/version of the program that compiled this compunit.", nullptr },
+  { "symtabs", cupy_get_symtabs, nullptr,
+    "List of symbol tables associated with this compunit", nullptr },
+  {nullptr}  /* Sentinel */
+};
+
+static PyMethodDef compunit_object_methods[] = {
+  { "is_valid", cupy_is_valid, METH_NOARGS,
+    "is_valid () -> Boolean.\n\
+Return true if this compunit is valid, false if not." },
+  { "global_block", cupy_global_block, METH_NOARGS,
+    "global_block () -> gdb.Block.\n\
+Return the global block of the compunit." },
+  { "static_block", cupy_static_block, METH_NOARGS,
+    "static_block () -> gdb.Block.\n\
+Return the static block of the compunit." },
+  {nullptr}  /* Sentinel */
+};
+
+PyTypeObject compunit_object_type = {
+  PyVarObject_HEAD_INIT (nullptr, 0)
+  "gdb.Compunit",		  /*tp_name*/
+  sizeof (compunit_object),	  /*tp_basicsize*/
+  0,				  /*tp_itemsize*/
+  cupy_dealloc,			  /*tp_dealloc*/
+  0,				  /*tp_print*/
+  0,				  /*tp_getattr*/
+  0,				  /*tp_setattr*/
+  0,				  /*tp_compare*/
+  0,				  /*tp_repr*/
+  0,				  /*tp_as_number*/
+  0,				  /*tp_as_sequence*/
+  0,				  /*tp_as_mapping*/
+  0,				  /*tp_hash */
+  0,				  /*tp_call*/
+  0,			          /*tp_str*/
+  0,				  /*tp_getattro*/
+  0,				  /*tp_setattro*/
+  0,				  /*tp_as_buffer*/
+  Py_TPFLAGS_DEFAULT,		  /*tp_flags*/
+  "GDB compunit object",	  /*tp_doc */
+  0,				  /*tp_traverse */
+  0,				  /*tp_clear */
+  gdbpy_richcompare<compunit_object, compunit_symtab,
+      &compunit_object::compunit>,/*tp_richcompare */
+  0,				  /*tp_weaklistoffset */
+  0,				  /*tp_iter */
+  0,				  /*tp_iternext */
+  compunit_object_methods,	  /*tp_methods */
+  0,				  /*tp_members */
+  compunit_object_getset,	  /*tp_getset */
+  0,				  /* tp_base */
+  0,				  /* tp_dict */
+  0,				  /* tp_descr_get */
+  0,				  /* tp_descr_set */
+  0,				  /* tp_dictoffset */
+  0,				  /* tp_init */
+  0,				  /* tp_alloc */
+  PyType_GenericNew		  /* tp_new */
+};
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index 6ce58a1f6ed..e006fcc6e75 100644
--- a/gdb/python/py-objfile.c
+++ b/gdb/python/py-objfile.c
@@ -544,6 +544,31 @@ objfpy_repr (PyObject *self_)
 			       objfile_name (obj));
 }
 
+/* Implementation of gdb.Objfile.compunits() -> List  */
+
+static PyObject *
+objfpy_compunits (PyObject *self_, PyObject *args)
+{
+  objfile_object *self = (objfile_object *) self_;
+
+  OBJFPY_REQUIRE_VALID (self);
+
+  gdbpy_ref<> list (PyList_New (0));
+  if (list == nullptr)
+    return nullptr;
+
+  for (struct compunit_symtab *compunit : self->objfile->compunits ())
+    {
+      gdbpy_ref<> item = compunit_to_compunit_object (compunit);
+
+      if (item.get () == nullptr
+	    || PyList_Append (list.get (), item.get ()) == -1)
+	return nullptr;
+    }
+
+  return list.release ();
+}
+
 /* Subroutine of gdbpy_lookup_objfile_by_build_id to simplify it.
    Return non-zero if STRING is a potentially valid build id.  */
 
@@ -737,6 +762,10 @@ Look up a global symbol in this objfile and return it." },
     "lookup_static_symbol (name [, domain]).\n\
 Look up a static-linkage global symbol in this objfile and return it." },
 
+  { "compunits", objfpy_compunits, METH_NOARGS,
+    "compunits () -> List.\n\
+Return a sequence of compunits associated to this objfile." },
+
   { NULL }
 };
 
diff --git a/gdb/python/py-symtab.c b/gdb/python/py-symtab.c
index 5330b15ef8d..dcb78e0045f 100644
--- a/gdb/python/py-symtab.c
+++ b/gdb/python/py-symtab.c
@@ -193,6 +193,18 @@ stpy_get_producer (PyObject *self, void *closure)
   Py_RETURN_NONE;
 }
 
+/* Getter function for Symtab.compunit.  */
+
+static PyObject *
+stpy_get_compunit (PyObject *self, void *closure)
+{
+  struct symtab *symtab = nullptr;
+
+  STPY_REQUIRE_VALID (self, symtab);
+
+  return compunit_to_compunit_object (symtab->compunit ()).release ();
+}
+
 static PyObject *
 stpy_fullname (PyObject *self, PyObject *args)
 {
@@ -533,6 +545,8 @@ static gdb_PyGetSetDef symtab_object_getset[] = {
     NULL },
   { "producer", stpy_get_producer, NULL,
     "The name/version of the program that compiled this symtab.", NULL },
+  { "compunit", stpy_get_compunit, nullptr,
+    "The compunit this symtab belongs to.", nullptr },
   {NULL}  /* Sentinel */
 };
 
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 4d4810dc4cc..0a52cb86b4a 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -555,6 +555,8 @@ gdbpy_ref<thread_object> create_thread_object (struct thread_info *tp);
 gdbpy_ref<> thread_to_thread_object (thread_info *thr);;
 gdbpy_ref<inferior_object> inferior_to_inferior_object (inferior *inf);
 
+gdbpy_ref<> compunit_to_compunit_object (struct compunit_symtab *compunit);
+
 PyObject *gdbpy_buffer_to_membuf (gdb::unique_xmalloc_ptr<gdb_byte> buffer,
 				  CORE_ADDR address, ULONGEST length);
 
@@ -571,6 +573,7 @@ struct symtab *symtab_object_to_symtab (PyObject *obj);
 struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);
 frame_info_ptr frame_object_to_frame_info (PyObject *frame_obj);
 struct gdbarch *arch_object_to_gdbarch (PyObject *obj);
+struct compunit_symtab *compunit_object_to_compunit (PyObject *obj);
 
 extern PyObject *gdbpy_execute_mi_command (PyObject *self, PyObject *args,
 					   PyObject *kw);
diff --git a/gdb/testsuite/gdb.python/py-block.exp b/gdb/testsuite/gdb.python/py-block.exp
index 20f21711126..64ee12786c4 100644
--- a/gdb/testsuite/gdb.python/py-block.exp
+++ b/gdb/testsuite/gdb.python/py-block.exp
@@ -104,6 +104,8 @@ gdb_test "python print (repr (block))" \
     "Check block in many_locals_func"
 gdb_test "python print (block.function)" "many_locals_func" \
     "many_locals_func block"
+gdb_test "python print(block.compunit)" "<gdb\.Compunit object at .*>" \
+    "test compunit property"
 
 # Switch frames, then test for main block.
 gdb_test "up" ".*"
diff --git a/gdb/testsuite/gdb.python/py-compunit.exp b/gdb/testsuite/gdb.python/py-compunit.exp
new file mode 100644
index 00000000000..450cd6c9f1b
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-compunit.exp
@@ -0,0 +1,62 @@
+# Copyright (C) 2024-2024 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 compunit
+# support in Python.
+
+load_lib gdb-python.exp
+
+require allow_python_tests
+
+standard_testfile py-objfile.c
+
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } {
+    return -1
+}
+
+if {![runto_main]} {
+    return 0
+}
+
+set python_error_text "Error occurred in Python.*"
+
+gdb_py_test_silent_cmd "python sym = gdb.lookup_symbol(\"some_var\")" \
+    "Find a symbol in objfile" 1
+gdb_py_test_silent_cmd "python objfile = sym\[0\].symtab.objfile" \
+    "Get backing object file" 1
+
+gdb_test "python print (len(objfile.compunits()) > 0)" \
+    "True" \
+    "Get objfile compunits"
+gdb_test "python print (objfile.compunits())" \
+    "\\\[<gdb\.Compunit object at .*>\\\]" \
+    "Objfile compunits return a sequence of gdb.Compunit"
+gdb_test "python print (objfile.compunits()\[0\] == objfile.compunits()\[0\])" \
+    "True" \
+    "Compunits are comparable"
+gdb_test "python print (len(objfile.compunits()\[0\].symtabs) > 0)" \
+    "True" \
+    "Get compunit symtabs"
+gdb_test "python print (objfile.compunits()\[0\].symtabs)" \
+    "\\\[<gdb\.Symtab.*>\\\]" \
+    "Compunit symtabs return a sequence of gdb.Symtab"
+
+
+gdb_unload "unload 1"
+
+gdb_test "python print (objfile.is_valid())" "False" \
+"Get objfile validity after unload"
+gdb_test "python print (objfile.compunits())" "RuntimeError.*: Objfile no longer exists.*" \
+"Get objfile compunits after unload"
\ No newline at end of file
diff --git a/gdb/testsuite/gdb.python/py-symtab.exp b/gdb/testsuite/gdb.python/py-symtab.exp
index 9caa5f1ebad..a949bf1e3b7 100644
--- a/gdb/testsuite/gdb.python/py-symtab.exp
+++ b/gdb/testsuite/gdb.python/py-symtab.exp
@@ -68,6 +68,8 @@ gdb_test "python print (sal.is_valid())" "True" "test sal.is_valid"
 gdb_test "python print (symtab.filename)" ".*${py_symbol_c}" "test symtab.filename"
 gdb_test "python print (symtab.objfile)" \
     "<gdb.Objfile filename=.*${testfile}.*>" "test symtab.objfile"
+gdb_test "python print (symtab.compunit)" \
+    "<gdb.Compunit .*>" "test symtab.compunit"
 gdb_test "python print (symtab.fullname())" ".*${full_py_symbol_c}" "test symtab.fullname"
 gdb_test "python print (symtab.is_valid())" "True" "test symtab.is_valid()"
 gdb_test "python print (\"qq\" in global_symbols)" "True" "test qq in global symbols"
@@ -110,6 +112,12 @@ gdb_test "python print (symtab != 123 )"\
     "True" \
     "test symtab non-equality with non-symtab"
 
+gdb_test "python print (symtab.compunit in symtab.objfile.compunits())" \
+    "True" "Test symtab.compunit in symtab.objfile.compunits()"
+gdb_test "python print (symtab.compunit.global_block() is symtab.global_block())" \
+    "True" "Test symtab.compunit.global_block() is symtab.global_block()"
+gdb_test "python print (symtab.compunit.static_block() is symtab.static_block())" \
+    "True" "Test symtab.compunit.static_block() is symtab.static_block()"
 
 # Test is_valid when the objfile is unloaded.  This must be the last
 # test as it unloads the object file in GDB.
-- 
2.45.2


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [RFC v2 11/21] gdb/python: allow instantiation of gdb.Objfile from Python
  2024-11-21 12:46 [RFC v2 00/21] Add Python "JIT" API Jan Vrany
                   ` (9 preceding siblings ...)
  2024-11-21 12:47 ` [RFC v2 10/21] gdb/python: add gdb.Compunit Jan Vrany
@ 2024-11-21 12:47 ` Jan Vrany
  2024-11-21 13:42   ` Eli Zaretskii
  2024-11-21 12:47 ` [RFC v2 12/21] gdb/python: add unlink () method to gdb.Objfile object Jan Vrany
                   ` (9 subsequent siblings)
  20 siblings, 1 reply; 39+ messages in thread
From: Jan Vrany @ 2024-11-21 12:47 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany, Eli Zaretskii

This commit adds code to allow user extension to instantiate
gdb.Objfile. This is a step towards a Python support for dynamically
generated code (JIT) in GDB.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
 gdb/doc/python.texi                     |  13 +++
 gdb/python/py-inferior.c                |  10 +++
 gdb/python/py-objfile.c                 | 111 +++++++++++++++++++++++-
 gdb/python/python-internal.h            |   1 +
 gdb/testsuite/gdb.python/py-objfile.exp |  38 +++++++-
 5 files changed, 169 insertions(+), 4 deletions(-)

diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 2065f001320..c32d84fb9ed 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -5656,6 +5656,19 @@ Reading symbols from ./hello...
 
 A @code{gdb.Objfile} object has the following methods:
 
+@defun Objfile.__init__ (filename @r{[}, inferior @r{][}, arch @r{]})
+Create a new objfile with given @var{filename}.
+
+The optional @var{inferior} argument specifies the inferior to which the newly
+created objfile is added to.  Defaults to currently selected inferior.
+@pxref{Inferiors In Python}.
+
+The optional @var{arch} argument specifies the architectore to associate with
+the newly created objfile.  Defaults to inferior's architecture.
+@xref{Architectures In Python}.
+
+@end defun
+
 @defun Objfile.is_valid ()
 Returns @code{True} if the @code{gdb.Objfile} object is valid,
 @code{False} if not.  A @code{gdb.Objfile} object can become invalid
diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c
index 60bf56d17de..725580770c7 100644
--- a/gdb/python/py-inferior.c
+++ b/gdb/python/py-inferior.c
@@ -225,6 +225,16 @@ python_free_objfile (struct objfile *objfile)
     gdbpy_print_stack ();
 }
 
+/* Return inferior reference that is wrapped by this object.  */
+
+inferior *
+inferior_object_to_inferior (PyObject *obj)
+{
+  if (! PyObject_TypeCheck (obj, &inferior_object_type))
+    return nullptr;
+  return ((inferior_object *) obj)->inferior;
+}
+
 /* Return a reference to the Python object of type Inferior
    representing INFERIOR.  If the object has already been created,
    return it and increment the reference count,  otherwise, create it.
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index e006fcc6e75..cc225ca135d 100644
--- a/gdb/python/py-objfile.c
+++ b/gdb/python/py-objfile.c
@@ -25,6 +25,7 @@
 #include "symtab.h"
 #include "python.h"
 #include "inferior.h"
+#include "observable.h"
 
 struct objfile_object
 {
@@ -251,6 +252,84 @@ objfpy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
   return (PyObject *) self.release ();
 }
 
+/* Object initializer; creates new a objfile.
+
+   Use: __init__(FILENAME [, INFERIOR [,ARCH]]).  */
+
+static int
+objfpy_init (PyObject *zelf, PyObject *args, PyObject *kw)
+{
+  struct objfile_object *self = (struct objfile_object*) zelf;
+
+  if (self->objfile)
+    {
+      PyErr_Format (PyExc_RuntimeError,
+		    _("Objfile object already initialized."));
+      return -1;
+    }
+
+  static const char *keywords[] = { "filename", "inferior", "arch", nullptr };
+  const char *filename;
+  PyObject* inf_obj = nullptr;
+  PyObject* arch_obj = nullptr;
+
+
+  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|OO", keywords,
+					&filename, &inf_obj, &arch_obj))
+    return -1;
+
+  inferior *inf = nullptr;
+  if (inf_obj)
+    {
+      inf = inferior_object_to_inferior (inf_obj);
+      if (! inf)
+	{
+	  PyErr_Format (PyExc_TypeError,
+		    _("The inferior argument is not gdb.Inferior object"));
+	  return -1;
+	}
+    }
+  else
+    {
+      inf = current_inferior ();
+    }
+
+  gdbarch *arch = nullptr;
+  if (arch_obj)
+    {
+      if (! gdbpy_is_architecture (arch_obj))
+	{
+	  PyErr_Format (PyExc_TypeError,
+		    _("The arch argument is not gdb.Architecture object"));
+	  return -1;
+	}
+      arch = arch_object_to_gdbarch (arch_obj);
+    }
+  else
+   {
+     arch = inf->arch ();
+   }
+
+  if (!objfpy_initialize (self))
+    {
+      PyErr_Format (PyExc_RuntimeError,
+		    _("Failed to initialize Objfile object."));
+      return -1;
+    }
+
+  struct objfile *objfile;
+
+  objfile = objfile::make (nullptr, inf->pspace, filename, OBJF_NOT_FILENAME | OBJF_READNOW);
+  objfile->per_bfd->gdbarch = arch;
+
+  self->objfile = objfile;
+  objfpy_objfile_data_key.set(objfile, self);
+  /* Increment refcount on self as it is now referenced from objfile!  */
+  Py_INCREF (self);
+
+  return 0;
+}
+
 PyObject *
 objfpy_get_printers (PyObject *o, void *ignore)
 {
@@ -731,6 +810,32 @@ objfile_to_objfile_object (struct objfile *objfile)
   return gdbpy_ref<>::new_reference (result);
 }
 
+/* This function remove any dynamic objfiles left over when the
+   inferior exits.  */
+
+static void
+objfpy_inferior_exit_hook (struct inferior *inf)
+{
+  for (objfile *objf : current_program_space->objfiles_safe ())
+    {
+      if (objf->obfd == nullptr)
+	{
+	  /* Following check is to only unlink dynamic objfiles created by
+	     Python code.  Dynamic objfiles created by JIT reader API are
+	     unlinked in jit_inferior_exit_hook ().  */
+	  if (objf->jited_data == nullptr || objf->jited_data->addr != 0)
+	    objf->unlink ();
+	}
+    }
+}
+
+void _initialize_py_objfile ();
+void
+_initialize_py_objfile ()
+{
+  gdb::observers::inferior_exit.attach (objfpy_inferior_exit_hook, "py-objfile");
+}
+
 static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_objfile (void)
 {
@@ -835,8 +940,8 @@ PyTypeObject objfile_object_type =
   0,				  /* tp_dict */
   0,				  /* tp_descr_get */
   0,				  /* tp_descr_set */
-  offsetof (objfile_object, dict), /* tp_dictoffset */
-  0,				  /* tp_init */
+  offsetof (objfile_object, dict),/* tp_dictoffset */
+  objfpy_init,	                  /* tp_init */
   0,				  /* tp_alloc */
-  objfpy_new,			  /* tp_new */
+  objfpy_new,   		  /* tp_new */
 };
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 0a52cb86b4a..a5bcfedf79b 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -574,6 +574,7 @@ struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);
 frame_info_ptr frame_object_to_frame_info (PyObject *frame_obj);
 struct gdbarch *arch_object_to_gdbarch (PyObject *obj);
 struct compunit_symtab *compunit_object_to_compunit (PyObject *obj);
+inferior *inferior_object_to_inferior(PyObject *obj);
 
 extern PyObject *gdbpy_execute_mi_command (PyObject *self, PyObject *args,
 					   PyObject *kw);
diff --git a/gdb/testsuite/gdb.python/py-objfile.exp b/gdb/testsuite/gdb.python/py-objfile.exp
index 2f5b7752b43..250ae4763b7 100644
--- a/gdb/testsuite/gdb.python/py-objfile.exp
+++ b/gdb/testsuite/gdb.python/py-objfile.exp
@@ -172,4 +172,40 @@ if ![ishost *-*-mingw*] {
 gdb_py_test_silent_cmd "python objfile = gdb.objfiles()\[0\]" \
     "get first objfile" 1
 gdb_file_cmd ${binfile}
-gdb_test "python print(objfile)" "<gdb.Objfile \\\(invalid\\\)>"
+gdb_test "python print(objfile)" "<gdb.Objfile \\\(invalid\\\)>" "print invalid objfile"
+
+# Test creating objfile dynamically from Python
+gdb_py_test_silent_cmd "python objfile = gdb.Objfile(\"Test objfile\")" \
+	"create objfile" 1
+
+gdb_test "python print(objfile)" \
+	"<gdb.Objfile filename=Test objfile>" \
+	"print dynamic objfile"
+
+gdb_test "python print(objfile.is_file)" \
+	"False" \
+	"(dynamic) objfile.is_file"
+
+gdb_test "python print(objfile.is_valid())" \
+	"True" \
+	"(dynamic) objfile.is_valid()"
+
+gdb_test "python print(objfile in gdb.objfiles())" \
+	"True" \
+	"(dynamic) objfile in gdb.objfiles()"
+
+gdb_test "python print( gdb.Objfile(\"Test objfile 2\", gdb.selected_inferior()))" \
+	"<gdb.Objfile filename=Test objfile 2>" \
+	"create objfile with inferior"
+
+gdb_test "python print( gdb.Objfile(\"Test objfile 3\", gdb.selected_inferior(), gdb.selected_inferior().architecture()))" \
+	"<gdb.Objfile filename=Test objfile 3>" \
+	"create objfile with inferior and arch"
+
+gdb_test "python print( gdb.Objfile(\"Test objfile 4\", gdb))" \
+	"TypeError.*:.*" \
+	"create objfile with invalid inferior"
+
+gdb_test "python print( gdb.Objfile(\"Test objfile 5\", gdb.selected_inferior(), gdb.selected_inferior()))" \
+	"TypeError.*:.*" \
+	"create objfile with valid inferior but invalid arch"
-- 
2.45.2


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [RFC v2 12/21] gdb/python: add unlink () method to gdb.Objfile object
  2024-11-21 12:46 [RFC v2 00/21] Add Python "JIT" API Jan Vrany
                   ` (10 preceding siblings ...)
  2024-11-21 12:47 ` [RFC v2 11/21] gdb/python: allow instantiation of gdb.Objfile from Python Jan Vrany
@ 2024-11-21 12:47 ` Jan Vrany
  2024-11-21 13:39   ` Eli Zaretskii
  2024-11-21 12:47 ` [RFC v2 13/21] gdb/python: allow instantiation of gdb.Compunit from Python Jan Vrany
                   ` (8 subsequent siblings)
  20 siblings, 1 reply; 39+ messages in thread
From: Jan Vrany @ 2024-11-21 12:47 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany, Eli Zaretskii

This commit adds method allowing one remove any objfile. This is meant
to be used to remove objfiles for dynamic code when this dynamic code
is discarded. However gdb.Objfile.unlink() makes no attempt to ensure
this - to make it consistent with other Python API to create and modify
objfiles related structures (compunits, symbol tables and so on).

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
 gdb/doc/python.texi                     |  6 ++++++
 gdb/python/py-objfile.c                 | 18 ++++++++++++++++++
 gdb/testsuite/gdb.python/py-objfile.exp | 12 ++++++++++++
 3 files changed, 36 insertions(+)

diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index c32d84fb9ed..f48803d08b4 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -5710,6 +5710,12 @@ Return a sequence of all the compunits associated with this objfile.
 @xref{Compunits In Python}.
 @end defun
 
+@defun Objfile.unlink ()
+Remove this objfile.  This should be used only on objfiles created by
+Python (see @code{Objfile.__init__} above) but @code{Objfile.unlink} does
+not make any checks.
+@end defun
+
 @node Frames In Python
 @subsubsection Accessing inferior stack frames from Python
 
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index cc225ca135d..f8b29ae1f47 100644
--- a/gdb/python/py-objfile.c
+++ b/gdb/python/py-objfile.c
@@ -608,6 +608,20 @@ objfpy_lookup_static_symbol (PyObject *self, PyObject *args, PyObject *kw)
   Py_RETURN_NONE;
 }
 
+/* Implementation of gdb.Objfile.unlink ().  */
+
+static PyObject *
+objfpy_unlink (PyObject *self, PyObject *args)
+{
+  objfile_object *obj = (objfile_object *) self;
+
+  OBJFPY_REQUIRE_VALID (obj);
+
+  obj->objfile->unlink();
+
+  Py_RETURN_NONE;
+}
+
 /* Implement repr() for gdb.Objfile.  */
 
 static PyObject *
@@ -871,6 +885,10 @@ Look up a static-linkage global symbol in this objfile and return it." },
     "compunits () -> List.\n\
 Return a sequence of compunits associated to this objfile." },
 
+  { "unlink", objfpy_unlink, METH_NOARGS,
+    "unlink ().\n\
+Remove this objfile." },
+
   { NULL }
 };
 
diff --git a/gdb/testsuite/gdb.python/py-objfile.exp b/gdb/testsuite/gdb.python/py-objfile.exp
index 250ae4763b7..7a88cac663d 100644
--- a/gdb/testsuite/gdb.python/py-objfile.exp
+++ b/gdb/testsuite/gdb.python/py-objfile.exp
@@ -209,3 +209,15 @@ gdb_test "python print( gdb.Objfile(\"Test objfile 4\", gdb))" \
 gdb_test "python print( gdb.Objfile(\"Test objfile 5\", gdb.selected_inferior(), gdb.selected_inferior()))" \
 	"TypeError.*:.*" \
 	"create objfile with valid inferior but invalid arch"
+
+gdb_test "python print(objfile.unlink())" \
+	"None" \
+	"remove (dynamic) objfile"
+
+gdb_test "python print(objfile in gdb.objfiles())" \
+	"False" \
+	"removed (dynamic) objfile no longer in gdb.objfiles()"
+
+gdb_test "python print(objfile.is_valid())" \
+	"False" \
+	"removes (dynamic) objfile is no longer valid"
-- 
2.45.2


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [RFC v2 13/21] gdb/python: allow instantiation of gdb.Compunit from Python
  2024-11-21 12:46 [RFC v2 00/21] Add Python "JIT" API Jan Vrany
                   ` (11 preceding siblings ...)
  2024-11-21 12:47 ` [RFC v2 12/21] gdb/python: add unlink () method to gdb.Objfile object Jan Vrany
@ 2024-11-21 12:47 ` Jan Vrany
  2024-11-21 13:40   ` Eli Zaretskii
  2024-11-21 12:47 ` [RFC v2 14/21] gdb/python: allow instantiation of gdb.Symtab " Jan Vrany
                   ` (7 subsequent siblings)
  20 siblings, 1 reply; 39+ messages in thread
From: Jan Vrany @ 2024-11-21 12:47 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany, Eli Zaretskii

This commit adds code to allow user extension to instantiate
gdb.Compunit. This is a step towards a Python support for dynamically
generated code (JIT) in GDB.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
 gdb/doc/python.texi                      | 18 ++++-
 gdb/python/py-compunit.c                 | 97 +++++++++++++++++++++++-
 gdb/python/py-objfile.c                  | 12 +++
 gdb/python/python-internal.h             |  1 +
 gdb/testsuite/gdb.python/py-compunit.exp | 42 +++++++++-
 5 files changed, 167 insertions(+), 3 deletions(-)

diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index f48803d08b4..115d5f04165 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -6551,7 +6551,7 @@ that objfile.  @xref{Objfiles In Python}.
 A @code{gdb.Compunit} object has the following attributes:
 
 @defvar Compunit.objfile
-The compunits' backing object file.  @xref{Objfiles In Python}.
+The compunit's backing object file.  @xref{Objfiles In Python}.
 This attribute is not writable.
 @end defvar
 
@@ -6570,6 +6570,22 @@ The list of symbol tables associated with this compunit.
 
 A @code{gdb.Compunit} object has the following methods:
 
+@defun Compunit.__init__ (filename, objfile, start, end @r{[}, capacity @r{]})
+Create a new compunit with given @var{filename} in given @var{objfile}
+(@pxref{Objfiles In Python}).  The newly created compunit has an empty global
+block and empty static block (@pxref{Blocks In Python}).
+
+The @var{start} and @var{end} arguments specifies the start and end address
+of compunit's global and static blocks.  It must not overlap with any existing
+compunit belonging to the same program space
+(@pxref{Progspaces In Python}).
+
+The optional @var{capacity} argument sets the initial capacity of the
+internal block vector.  More blocks than @var{capacity} can still be added
+to the compunit however.  If not specified, defaults to 8 blocks (including
+global and static blocks).
+@end defun
+
 @defun Compunit.is_valid ()
 Returns @code{True} if the @code{gdb.Compunit} object is valid,
 @code{False} if not.  A @code{gdb.Compunit} object can become invalid if
diff --git a/gdb/python/py-compunit.c b/gdb/python/py-compunit.c
index 829746cc92d..c7e89643abc 100644
--- a/gdb/python/py-compunit.c
+++ b/gdb/python/py-compunit.c
@@ -17,6 +17,7 @@
    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 <algorithm>
 #include "charset.h"
 #include "symtab.h"
 #include "source.h"
@@ -217,6 +218,100 @@ set_compunit (compunit_object *obj, struct compunit_symtab *compunit)
     obj->next = nullptr;
 }
 
+/* Object initializer; creates a new compunit.
+
+   Use: __init__(FILENAME, OBJFILE, START, END [, CAPACITY]).  */
+
+static int
+cupy_init (PyObject *zelf, PyObject *args, PyObject *kw)
+{
+  struct compunit_object *self = (struct compunit_object*) zelf;
+
+  if (self->compunit)
+    {
+      PyErr_Format (PyExc_RuntimeError,
+		    _("Compunit object already initialized."));
+      return -1;
+    }
+
+  static const char *keywords[] = { "filename", "objfile", "start", "end",
+				    "capacity", nullptr };
+  const char *filename;
+  PyObject *objf_obj = nullptr;
+  uint64_t start = 0;
+  uint64_t end = 0;
+  uint64_t capacity = 8;
+
+
+  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "sOKK|K", keywords,
+					&filename, &objf_obj, &start, &end,
+					&capacity))
+    return -1;
+
+  auto objf = objfile_object_to_objfile (objf_obj);
+  if (! objf)
+    {
+      PyErr_Format (PyExc_TypeError,
+		    _("The objfile argument is not valid gdb.Objfile 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 to-be created compunits' global block does not overlap
+     with existing compunit.  */
+
+  for (struct objfile *of : objf->pspace ()->objfiles_safe ())
+    {
+      for (compunit_symtab *cu : of->compunits ())
+	{
+	  global_block *gb = cu->blockvector ()->global_block ();
+	  if (std::max (start, gb->start ()) < std::min(end, gb->end ()))
+	    {
+	      PyErr_Format (PyExc_ValueError,
+		    _("The start-end range overlaps with existing compunit"));
+	      return -1;
+	    }
+	}
+    }
+
+  blockvector *bv = allocate_blockvector (&objf->objfile_obstack,
+					  FIRST_LOCAL_BLOCK, capacity);
+  compunit_symtab *cu = allocate_compunit_symtab (objf, filename);
+  cu->set_dirname (nullptr);
+  cu->set_blockvector (bv);
+
+  /* Allocate global block. */
+  global_block *gb = new (&objf->objfile_obstack) global_block ();
+  gb->set_multidict (mdict_create_linear_expandable (language_minimal));
+  gb->set_start ((CORE_ADDR) start);
+  gb->set_end ((CORE_ADDR) end);
+  gb->set_compunit (cu);
+  bv->set_block (GLOBAL_BLOCK, gb);
+
+  /* Allocate static block.  */
+  struct block *sb = new (&objf->objfile_obstack) block ();
+  sb->set_multidict (mdict_create_linear_expandable (language_minimal));
+  sb->set_start ((CORE_ADDR) start);
+  sb->set_end ((CORE_ADDR) end);
+  sb->set_superblock (gb);
+  bv->set_block (STATIC_BLOCK, sb);
+
+  add_compunit_symtab_to_objfile (cu);
+
+  set_compunit(self, cu);
+
+  return 0;
+}
+
 /* Return a new reference to gdb.Compunit Python object representing
    COMPUNIT.  Return NULL and set the Python error on failure.  */
 gdbpy_ref<>
@@ -313,7 +408,7 @@ PyTypeObject compunit_object_type = {
   0,				  /* tp_descr_get */
   0,				  /* tp_descr_set */
   0,				  /* tp_dictoffset */
-  0,				  /* tp_init */
+  cupy_init,                      /* tp_init */
   0,				  /* tp_alloc */
   PyType_GenericNew		  /* tp_new */
 };
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index f8b29ae1f47..21f46260e74 100644
--- a/gdb/python/py-objfile.c
+++ b/gdb/python/py-objfile.c
@@ -824,6 +824,18 @@ objfile_to_objfile_object (struct objfile *objfile)
   return gdbpy_ref<>::new_reference (result);
 }
 
+/* Returns the struct objfile value corresponding to the given Python
+   objfile object OBJ.  Returns NULL if OBJ is not an objfile object.  */
+
+struct objfile *
+objfile_object_to_objfile (PyObject *obj)
+{
+  if (! PyObject_TypeCheck (obj, &objfile_object_type))
+    return nullptr;
+
+  return ((objfile_object *)obj)->objfile;
+}
+
 /* This function remove any dynamic objfiles left over when the
    inferior exits.  */
 
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index a5bcfedf79b..88432e04f47 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -575,6 +575,7 @@ frame_info_ptr frame_object_to_frame_info (PyObject *frame_obj);
 struct gdbarch *arch_object_to_gdbarch (PyObject *obj);
 struct compunit_symtab *compunit_object_to_compunit (PyObject *obj);
 inferior *inferior_object_to_inferior(PyObject *obj);
+struct objfile *objfile_object_to_objfile(PyObject *obj);
 
 extern PyObject *gdbpy_execute_mi_command (PyObject *self, PyObject *args,
 					   PyObject *kw);
diff --git a/gdb/testsuite/gdb.python/py-compunit.exp b/gdb/testsuite/gdb.python/py-compunit.exp
index 450cd6c9f1b..cf9197988d7 100644
--- a/gdb/testsuite/gdb.python/py-compunit.exp
+++ b/gdb/testsuite/gdb.python/py-compunit.exp
@@ -54,9 +54,49 @@ gdb_test "python print (objfile.compunits()\[0\].symtabs)" \
     "Compunit symtabs return a sequence of gdb.Symtab"
 
 
+# Test creation of compunits
+gdb_py_test_silent_cmd "python cu = gdb.Compunit(\"compunit1\", objfile, 200, 300)" \
+    "Create compunit1" 1
+gdb_test "python print(cu)" \
+    "<gdb\.Compunit object at .*>" \
+    "Print created compunit1"
+gdb_test "python print(cu in objfile.compunits())" \
+    "True" \
+    "Test compunit1 is in objfile.compunits()"
+gdb_test "python print(cu.global_block().start)" \
+    "0" \
+    "Test compunit1.global_block().start is 0"
+gdb_test "python print(cu.global_block().end)" \
+    "0" \
+    "Test compunit1.global_block().end is 0"
+gdb_test "python print(cu.static_block().start)" \
+    "0" \
+    "Test compunit1.static_block().start is 0"
+gdb_test "python print(cu.static_block().end)" \
+    "0" \
+    "Test compunit1.static_block().end is 0"
+
+gdb_py_test_silent_cmd "python cu2 = gdb.Compunit(\"dynamic2\", objfile, 400, 500, 24)" \
+    "Create compunit2 with capacity fo 24 blocks" 1
+
+gdb_test "python cu3 = gdb.Compunit(\"dynamic3\", gdb, 600, 700)" \
+    "TypeError.*:.*" \
+    "Create compunit3 passing non-objfile"
+
+gdb_test "python cu4 = gdb.Compunit(\"dynamic4\", objfile, 900, 800)" \
+    "ValueError.*:.*" \
+    "Create compunit4 passing invalid global block range"
+
+gdb_test "python cu5 = gdb.Compunit(\"dynamic5\", objfile, 225, 325)" \
+    "ValueError.*:.*" \
+    "Create compunit4 passing overlapping global block range"
+
+
 gdb_unload "unload 1"
 
 gdb_test "python print (objfile.is_valid())" "False" \
 "Get objfile validity after unload"
 gdb_test "python print (objfile.compunits())" "RuntimeError.*: Objfile no longer exists.*" \
-"Get objfile compunits after unload"
\ No newline at end of file
+"Get objfile compunits after unload"
+gdb_py_test_silent_cmd "python cu6 = gdb.Compunit(\"dynamic6\", objfile)" \
+    "Create compunit4 passing invalid objfile" 1
\ No newline at end of file
-- 
2.45.2


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [RFC v2 14/21] gdb/python: allow instantiation of gdb.Symtab from Python
  2024-11-21 12:46 [RFC v2 00/21] Add Python "JIT" API Jan Vrany
                   ` (12 preceding siblings ...)
  2024-11-21 12:47 ` [RFC v2 13/21] gdb/python: allow instantiation of gdb.Compunit from Python Jan Vrany
@ 2024-11-21 12:47 ` Jan Vrany
  2024-11-21 13:43   ` Eli Zaretskii
  2024-11-21 12:47 ` [RFC v2 15/21] gdb/python: allow instantiation of gdb.Block " Jan Vrany
                   ` (6 subsequent siblings)
  20 siblings, 1 reply; 39+ messages in thread
From: Jan Vrany @ 2024-11-21 12:47 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany, Eli Zaretskii

This commit adds code to allow user extension to instantiate
gdb.Symtab. This is a step towards a Python support for dynamically
generated code (JIT) in GDB.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
 gdb/doc/python.texi                    |  4 ++
 gdb/python/py-symtab.c                 | 53 +++++++++++++++++++++++++-
 gdb/testsuite/gdb.python/py-symtab.exp | 15 ++++++++
 3 files changed, 70 insertions(+), 2 deletions(-)

diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 115d5f04165..12da5135484 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -6508,6 +6508,10 @@ This attribute is not writable.
 
 A @code{gdb.Symtab} object has the following methods:
 
+@defun Symtab.__init__ (filename, compunit)
+Create a new symtab for given @var{filename} in given @var{compunit}.
+@end defun
+
 @defun Symtab.is_valid ()
 Returns @code{True} if the @code{gdb.Symtab} object is valid,
 @code{False} if not.  A @code{gdb.Symtab} object can become invalid if
diff --git a/gdb/python/py-symtab.c b/gdb/python/py-symtab.c
index dcb78e0045f..9e5bae69d22 100644
--- a/gdb/python/py-symtab.c
+++ b/gdb/python/py-symtab.c
@@ -136,6 +136,8 @@ static const registry<objfile>::key<sal_object, salpy_deleter>
 	}								\
   } while (0)
 
+static void set_symtab (symtab_object *obj, struct symtab *symtab);
+
 static PyObject *
 stpy_str (PyObject *self)
 {
@@ -279,6 +281,46 @@ stpy_get_linetable (PyObject *self, PyObject *args)
   return symtab_to_linetable_object (self);
 }
 
+/* Object initializer; creates new symtab.
+
+   Use: __init__(FILENAME, COMPUNIT).  */
+
+static int
+stpy_init (PyObject *zelf, PyObject *args, PyObject *kw)
+{
+  struct symtab_object *self = (struct symtab_object*) zelf;
+
+  if (self->symtab)
+    {
+      PyErr_Format (PyExc_RuntimeError,
+		    _("Symtab object already initialized."));
+      return -1;
+    }
+
+   static const char *keywords[] = { "filename", "compunit", nullptr };
+   const char *filename;
+   PyObject *cu_obj;
+
+   if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "sO", keywords,
+					 &filename, &cu_obj))
+    return -1;
+
+
+  compunit_symtab *cu = compunit_object_to_compunit (cu_obj);
+  if (cu == nullptr)
+    {
+      PyErr_Format (PyExc_TypeError,
+		    _("The compunit argument is not valid gdb.Compunit "
+		      "object"));
+      return -1;
+    }
+
+  struct symtab *symtab = allocate_symtab (cu, filename);
+  set_symtab (self, symtab);
+
+  return 0;
+}
+
 static PyObject *
 salpy_str (PyObject *self)
 {
@@ -523,7 +565,6 @@ symtab_object_to_symtab (PyObject *obj)
 static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_symtabs (void)
 {
-  symtab_object_type.tp_new = PyType_GenericNew;
   if (gdbpy_type_ready (&symtab_object_type) < 0)
     return -1;
 
@@ -600,7 +641,15 @@ PyTypeObject symtab_object_type = {
   0,				  /*tp_iternext */
   symtab_object_methods,	  /*tp_methods */
   0,				  /*tp_members */
-  symtab_object_getset		  /*tp_getset */
+  symtab_object_getset,		  /*tp_getset */
+  0,				  /*tp_base */
+  0,				  /*tp_dict */
+  0,				  /*tp_descr_get */
+  0,				  /*tp_descr_set */
+  0,                              /*tp_dictoffset */
+  stpy_init,	                  /*tp_init */
+  0,				  /*tp_alloc */
+  PyType_GenericNew,		  /*tp_new */
 };
 
 static gdb_PyGetSetDef sal_object_getset[] = {
diff --git a/gdb/testsuite/gdb.python/py-symtab.exp b/gdb/testsuite/gdb.python/py-symtab.exp
index a949bf1e3b7..993f651f291 100644
--- a/gdb/testsuite/gdb.python/py-symtab.exp
+++ b/gdb/testsuite/gdb.python/py-symtab.exp
@@ -119,6 +119,17 @@ gdb_test "python print (symtab.compunit.global_block() is symtab.global_block())
 gdb_test "python print (symtab.compunit.static_block() is symtab.static_block())" \
     "True" "Test symtab.compunit.static_block() is symtab.static_block()"
 
+# Test creating new symtab in existing compunit
+gdb_py_test_silent_cmd "python cu = symtab.compunit" "remember compunit" 1
+gdb_test "python print(repr( gdb.Symtab(\"some_file.txt\", cu) ))" \
+    "<gdb.Symtab object at .*>" \
+    "test creation of symtab"
+
+gdb_test "python print(repr( gdb.Symtab(\"some_file.txt\", (1,2,3)) ))" \
+    "TypeError.*:.*" \
+    "test creation of symtab passing non-compunit object"
+
+
 # Test is_valid when the objfile is unloaded.  This must be the last
 # test as it unloads the object file in GDB.
 gdb_unload
@@ -129,3 +140,7 @@ gdb_test "python print (symtab.is_valid())" "False" \
 
 gdb_test_no_output "python sal = None" "test sal destructor"
 gdb_test_no_output "python symtab = None" "test symtab destructor"
+
+gdb_test "python print(repr( gdb.Symtab(\"some_file2.txt\", cu) ))" \
+    "TypeError.*:.*" \
+    "test creation of symtab passing invalid compunit"
-- 
2.45.2


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [RFC v2 15/21] gdb/python: allow instantiation of gdb.Block from Python
  2024-11-21 12:46 [RFC v2 00/21] Add Python "JIT" API Jan Vrany
                   ` (13 preceding siblings ...)
  2024-11-21 12:47 ` [RFC v2 14/21] gdb/python: allow instantiation of gdb.Symtab " Jan Vrany
@ 2024-11-21 12:47 ` Jan Vrany
  2024-11-21 13:35   ` Eli Zaretskii
  2024-11-21 12:47 ` [RFC v2 16/21] gdb/python: allow instantiation of gdb.Symbol " Jan Vrany
                   ` (5 subsequent siblings)
  20 siblings, 1 reply; 39+ messages in thread
From: Jan Vrany @ 2024-11-21 12:47 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany, Eli Zaretskii

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 <eliz@gnu.org>
---
 gdb/doc/python.texi                   |   6 ++
 gdb/python/py-block.c                 | 111 +++++++++++++++++++++++++-
 gdb/testsuite/gdb.python/py-block.exp |  23 ++++++
 3 files changed, 138 insertions(+), 2 deletions(-)

diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 12da5135484..8b3c95cbf1d 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))" "<gdb.Block main \{.*\}>" \
          "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))" \
+	 "<gdb.Block <anonymous> \{.*\}>" \
+	 "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))" \
+	 "<gdb.Block <anonymous> \{.*\}>" \
+	 "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
-- 
2.45.2


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [RFC v2 16/21] gdb/python: allow instantiation of gdb.Symbol from Python
  2024-11-21 12:46 [RFC v2 00/21] Add Python "JIT" API Jan Vrany
                   ` (14 preceding siblings ...)
  2024-11-21 12:47 ` [RFC v2 15/21] gdb/python: allow instantiation of gdb.Block " Jan Vrany
@ 2024-11-21 12:47 ` Jan Vrany
  2024-11-21 13:37   ` Eli Zaretskii
  2024-11-21 12:47 ` [RFC v2 17/21] gdb/python: add add_symbol () method to gdb.Block Jan Vrany
                   ` (4 subsequent siblings)
  20 siblings, 1 reply; 39+ messages in thread
From: Jan Vrany @ 2024-11-21 12:47 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany, Eli Zaretskii

This commit adds code to allow user extension to instantiate
gdb.Symbol.

As of now only "function" symbols can be created (that is: symbols
of FUNCTION_DOMAIN and with address class LOC_BLOCK). This is enough
to be able to implement "JIT reader" equivalent in Python. Future
commits may extend this API to allow creation of other kinds of symbols
(static variables, arguments, locals and so on).

Like previous similar commits, this is a step towards a Python support
for dynamically generated code (JIT) in GDB.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
 gdb/doc/python.texi                    |  26 +++++
 gdb/objfiles.c                         |  20 ++++
 gdb/objfiles.h                         |   4 +
 gdb/python/py-symbol.c                 | 139 ++++++++++++++++++++++++-
 gdb/testsuite/gdb.python/py-symbol.exp |  13 +++
 5 files changed, 201 insertions(+), 1 deletion(-)

diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 8b3c95cbf1d..55ca91920cb 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -6278,6 +6278,32 @@ arguments.
 
 A @code{gdb.Symbol} object has the following methods:
 
+@defun Symbol.__init__ (name, symtab, type, domain, addr_class, value)
+Creates new symbol named @var{name} and adds it to symbol table
+@var{symtab}.
+
+The @var{type} argument specifies type of the symbol as @var{gdb.Type}
+object (@pxref{Types In Python}).
+
+The @var{domain} argument specifies domain of the symbol.  Each domain is
+a constant defined in the @code{gdb} module and described later in this
+chapter.
+
+The @var{addr_class} argument, together with @var{value} argument, specifies
+how to find the value of this symbol.  Each address class is a constant
+defined in the @code{gdb} module and described later in this chapter.  As of
+now, only @code{gdb.SYMBOL_LOC_BLOCK} address class is supported, but future
+versions of @value{GDBN} may support more address classes.
+
+The meaning of @var{value} argument depends on the value of @var{addr_class}:
+@vtable @code
+@item gdb.SYMBOL_LOC_BLOCK
+The @var{value} argument must be a block (a @code{gdb.Block} object).  Block
+must belong to the same compunit as the
+@var{symtab} parameter (@pxref{Compunits In Python}).
+@end vtable
+@end defun
+
 @defun Symbol.is_valid ()
 Returns @code{True} if the @code{gdb.Symbol} object is valid,
 @code{False} if not.  A @code{gdb.Symbol} object can become invalid if
diff --git a/gdb/objfiles.c b/gdb/objfiles.c
index 0bb578fa6a8..cdb6dba2f7c 100644
--- a/gdb/objfiles.c
+++ b/gdb/objfiles.c
@@ -1312,3 +1312,23 @@ objfile_int_type (struct objfile *of, int size_in_bytes, bool unsigned_p)
 
   gdb_assert_not_reached ("unable to find suitable integer type");
 }
+
+/* See objfiles.h.  */
+
+int
+objfile::find_section_index (CORE_ADDR start, CORE_ADDR end)
+{
+  obj_section *sect;
+  int sect_index;
+  for (sect = this->sections_start, sect_index = 0;
+       sect < this->sections_end;
+       sect++, sect_index++)
+    {
+      if (sect->the_bfd_section == nullptr)
+	continue;
+
+      if (sect->addr () <= start && end <= sect->endaddr ())
+	return sect_index;
+    }
+  return -1;
+}
\ No newline at end of file
diff --git a/gdb/objfiles.h b/gdb/objfiles.h
index bd65e2bd030..94533797563 100644
--- a/gdb/objfiles.h
+++ b/gdb/objfiles.h
@@ -644,6 +644,10 @@ struct objfile : intrusive_list_node<objfile>
     this->section_offsets[idx] = offset;
   }
 
+  /* Return the section index for section mapped at memory range
+     [START, END].  If there's no such section, return -1.  */
+  int find_section_index (CORE_ADDR start, CORE_ADDR end);
+
   class section_iterator
   {
   public:
diff --git a/gdb/python/py-symbol.c b/gdb/python/py-symbol.c
index 44bed85481b..78db88333c5 100644
--- a/gdb/python/py-symbol.c
+++ b/gdb/python/py-symbol.c
@@ -400,6 +400,135 @@ sympy_repr (PyObject *self)
 			       symbol->print_name ());
 }
 
+/* Object initializer; creates new symbol.
+
+   Use: __init__(NAME, SYMTAB, TYPE, DOMAIN, ADDR_CLASS, VALUE).  */
+
+static int
+sympy_init (PyObject *zelf, PyObject *args, PyObject *kw)
+{
+  struct symbol_object *self = (struct symbol_object*) zelf;
+
+  if (self->symbol)
+    {
+      PyErr_Format (PyExc_RuntimeError,
+		    _("Symbol object already initialized."));
+      return -1;
+    }
+
+   static const char *keywords[] = { "name", "symtab", "type",
+				     "domain", "addr_class", "value",
+				     nullptr };
+   const char *name;
+   PyObject *symtab_obj = nullptr;
+   PyObject *type_obj = nullptr;
+   domain_enum domain;
+   unsigned int addr_class;
+   PyObject *value_obj = nullptr;
+
+   if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "sOOIIO", keywords,
+					 &name, &symtab_obj, &type_obj,
+					 &domain, &addr_class, &value_obj))
+    return -1;
+
+
+  struct symtab *symtab = symtab_object_to_symtab (symtab_obj);
+  if (symtab == nullptr)
+    {
+      PyErr_Format (PyExc_TypeError,
+		    _("The symtab argument is not valid gdb.Symtab object"));
+      return -1;
+    }
+
+  struct type *type = type_object_to_type (type_obj);
+  if (type == nullptr)
+    {
+      PyErr_Format (PyExc_TypeError,
+		    _("The type argument is not valid gdb.Type object"));
+      return -1;
+    }
+  if (type->objfile_owner () != nullptr &&
+      type->objfile_owner () != symtab->compunit ()->objfile ())
+    {
+      PyErr_Format (PyExc_ValueError,
+		    _("The type argument's owning objfile differs from "
+		      "symtab's objfile."));
+      return -1;
+    }
+
+  union _value {
+    const struct block *block;
+  } value;
+
+  switch (addr_class)
+    {
+      default:
+	PyErr_Format (PyExc_ValueError,
+		      _("The value of addr_class argument is not supported"));
+	return -1;
+
+      case LOC_BLOCK:
+	if ((value.block = block_object_to_block (value_obj)) == nullptr)
+	  {
+	    PyErr_Format (PyExc_TypeError,
+			 _("The addr_class argument is SYMBOL_LOC_BLOCK but "
+			   "the value argument is not a valid gdb.Block."));
+	    return -1;
+	  }
+	if (type->code () != TYPE_CODE_FUNC)
+	  {
+	    PyErr_Format (PyExc_ValueError,
+			 _("The addr_class argument is SYMBOL_LOC_BLOCK but "
+			   "the type argument is not a function type."));
+	    return -1;
+	  }
+	break;
+    }
+
+  struct objfile *objfile = symtab->compunit ()->objfile ();
+  auto_obstack *obstack = &(objfile->objfile_obstack);
+  struct symbol *sym = new (obstack) symbol();
+
+  sym->m_name = obstack_strdup (obstack, name);
+  sym->set_symtab (symtab);
+  sym->set_type (type);
+  sym->set_domain (domain);
+  sym->set_aclass_index (addr_class);
+
+  switch (addr_class)
+    {
+      case LOC_BLOCK:
+	{
+	  sym->set_value_block (value.block);
+
+	  if (domain == FUNCTION_DOMAIN)
+	    const_cast<struct block*> (value.block)->set_function (sym);
+
+	  /* Set symbol's section index.  This needed in somewhat unusual
+	     usecase where dynamic code is generated into a special section
+	     (defined in custom linker script or otherwise).  Otherwise,
+	     find_pc_sect_compunit_symtab () would not find the compunit
+	     symtab and commands like "disassemble function_name" would
+	     resort to disassemble complete section.
+
+	     Note that in usual case where new objfile is created for
+	     dynamic code, the objfile has no sections at all and
+	     objfile::find_section_index () returns -1.
+	     */
+	  CORE_ADDR start = value.block->start ();
+	  CORE_ADDR end = value.block->end ();
+	  sym->set_section_index (objfile->find_section_index (start, end));
+	}
+	break;
+      default:
+	gdb_assert_not_reached("unreachable");
+	break;
+  }
+
+  set_symbol (self, sym);
+  return 0;
+}
+
 /* Implementation of
    gdb.lookup_symbol (name [, block] [, domain]) -> (symbol, is_field_of_this)
    A tuple with 2 elements is always returned.  The first is the symbol
@@ -774,5 +903,13 @@ PyTypeObject symbol_object_type = {
   0,				  /*tp_iternext */
   symbol_object_methods,	  /*tp_methods */
   0,				  /*tp_members */
-  symbol_object_getset		  /*tp_getset */
+  symbol_object_getset,		  /*tp_getset */
+  0,				  /*tp_base */
+  0,				  /*tp_dict */
+  0,				  /*tp_descr_get */
+  0,				  /*tp_descr_set */
+  0,                              /*tp_dictoffset */
+  sympy_init,	                  /*tp_init */
+  0,				  /*tp_alloc */
+  PyType_GenericNew,		  /*tp_new */
 };
diff --git a/gdb/testsuite/gdb.python/py-symbol.exp b/gdb/testsuite/gdb.python/py-symbol.exp
index 1bfa17b4e91..d9c0e255146 100644
--- a/gdb/testsuite/gdb.python/py-symbol.exp
+++ b/gdb/testsuite/gdb.python/py-symbol.exp
@@ -222,6 +222,19 @@ gdb_test "python print (t\[0\] != 123 )"\
 	 "True" \
 	 "test symbol non-equality with non-symbol"
 
+# Test creation of new symbols
+gdb_py_test_silent_cmd "python s = gdb.Symbol(\"ns1\", t\[0\].symtab, t\[0\].type.function(), gdb.SYMBOL_FUNCTION_DOMAIN, gdb.SYMBOL_LOC_BLOCK, t\[0\].symtab.static_block() )" \
+	"create symbol" 0
+gdb_test "python print (s)" \
+	 "ns1" \
+	 "test new symbol's __str__"
+gdb_test "python print (s.symtab == t\[0\].symtab)" \
+	 "True" \
+	 "test new symbol's symtab"
+gdb_test "python print (s.type == t\[0\].type.function())" \
+	 "True" \
+	 "test new symbol's type"
+
 # C++ tests
 # Recompile binary.
 lappend opts c++
-- 
2.45.2


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [RFC v2 17/21] gdb/python: add add_symbol () method to gdb.Block
  2024-11-21 12:46 [RFC v2 00/21] Add Python "JIT" API Jan Vrany
                   ` (15 preceding siblings ...)
  2024-11-21 12:47 ` [RFC v2 16/21] gdb/python: allow instantiation of gdb.Symbol " Jan Vrany
@ 2024-11-21 12:47 ` Jan Vrany
  2024-11-21 13:36   ` Eli Zaretskii
  2024-11-21 12:47 ` [RFC v2 18/21] gdb/python: add more attributes to gdb.LinetableEntry objects Jan Vrany
                   ` (3 subsequent siblings)
  20 siblings, 1 reply; 39+ messages in thread
From: Jan Vrany @ 2024-11-21 12:47 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany, Eli Zaretskii

This commit adds new method add_symbol () to gdb.Block objects.
A typical use of it is to add previously instantiated gdb.Symbol object
to block when interfacing with JIT compiler.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
 gdb/doc/python.texi                   |  5 +++
 gdb/python/py-block.c                 | 52 +++++++++++++++++++++++----
 gdb/testsuite/gdb.python/py-block.exp | 20 +++++++++++
 3 files changed, 71 insertions(+), 6 deletions(-)

diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 55ca91920cb..43109dc6554 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -6043,6 +6043,11 @@ 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.add_symbol (symbol)
+Add @var{symbol} to this block.  Both the block and the @var{symbol} must
+belong to the same compunit (@pxref{Compunits In Python}).
+@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 626ed10deb6..f3b635f64b8 100644
--- a/gdb/python/py-block.c
+++ b/gdb/python/py-block.c
@@ -261,6 +261,43 @@ blpy_is_static (PyObject *self, void *closure)
   Py_RETURN_FALSE;
 }
 
+/* Implementation of gdb.Block.add_symbol (self, symbol).
+   Adds SYMBOL to this block.  */
+
+static PyObject *
+blpy_add_symbol (PyObject *self, PyObject *symbol_obj)
+{
+  const struct block *block;
+
+  BLPY_REQUIRE_VALID (self, block);
+
+  struct symbol *symbol = symbol_object_to_symbol (symbol_obj);
+  if (symbol == nullptr)
+    {
+      return PyErr_Format (PyExc_TypeError,
+			   _("The symbol argument is not valid gdb.Symbol"));
+    }
+
+  if (symbol->symtab ()->compunit() != block->global_block ()->compunit ())
+    {
+      return PyErr_Format (PyExc_TypeError,
+			   _("The symbol argument belongs to different "
+			     "compunit than block"));
+    }
+
+  multidictionary *dict = block->multidict ();
+  if (dict == nullptr)
+    {
+      auto_obstack *obstack =
+	&(block->global_block ()->compunit ()->objfile ()->objfile_obstack);
+      dict = mdict_create_linear (obstack, nullptr);
+      const_cast<struct block *>(block)->set_multidict (dict);
+    }
+
+  mdict_add_symbol (dict, symbol);
+  Py_RETURN_NONE;
+}
+
 /* Given a string, returns the gdb.Symbol representing that symbol in this
    block.  If such a symbol does not exist, returns NULL with a Python
    exception.  */
@@ -358,7 +395,7 @@ blpy_init (PyObject *zelf, PyObject *args, PyObject *kw)
       return -1;
     }
 
-  static const char *keywords[] = { "superblock", "start", "end", NULL };
+  static const char *keywords[] = { "superblock", "start", "end", nullptr };
   PyObject *superblock_obj;
   uint64_t start;
   uint64_t end;
@@ -398,9 +435,9 @@ blpy_init (PyObject *zelf, PyObject *args, PyObject *kw)
 
   /* Check that start-end range does not overlap with any
      "sibling" blocks' range.  */
-  auto cu = superblock->global_block ()->compunit ();
+  compunit_symtab *cu = superblock->global_block ()->compunit ();
 
-  for (auto each : cu->blockvector ()->blocks ())
+  for (const struct block *each : cu->blockvector ()->blocks ())
     {
       if (each->superblock () == superblock)
 	{
@@ -415,11 +452,11 @@ blpy_init (PyObject *zelf, PyObject *args, PyObject *kw)
 	}
     }
 
-  auto obstack = &(cu->objfile ()->objfile_obstack);
-  auto blk = new (obstack) block ();
+  auto_obstack *obstack = &(cu->objfile ()->objfile_obstack);
+  struct block *blk = new (obstack) block ();
 
   blk->set_superblock (superblock);
-  blk->set_multidict (mdict_create_linear (obstack, NULL));
+  blk->set_multidict (mdict_create_linear (obstack, nullptr));
   blk->set_start ((CORE_ADDR) start);
   blk->set_end ((CORE_ADDR) end);
 
@@ -651,6 +688,9 @@ static PyMethodDef block_object_methods[] = {
   { "is_valid", blpy_is_valid, METH_NOARGS,
     "is_valid () -> Boolean.\n\
 Return true if this block is valid, false if not." },
+  { "add_symbol", blpy_add_symbol, METH_O,
+    "add_symbol (symbol) -> None.\n\
+Add given symbol to the block." },
   {NULL}  /* Sentinel */
 };
 
diff --git a/gdb/testsuite/gdb.python/py-block.exp b/gdb/testsuite/gdb.python/py-block.exp
index 3c5d291edb0..6bc98e9563f 100644
--- a/gdb/testsuite/gdb.python/py-block.exp
+++ b/gdb/testsuite/gdb.python/py-block.exp
@@ -138,6 +138,26 @@ gdb_test "python print ( gdb.Block(cu.static_block(), 160, 170))" \
 	 "<gdb.Block <anonymous> \{.*\}>" \
 	 "Create sibling block"
 
+# Test adding symbols to a block.
+gdb_py_test_silent_cmd "python symtab = gdb.Symtab(\"some_file.txt\", cu)" \
+		       "Create new symtab" 1
+gdb_py_test_silent_cmd "python typ = gdb.selected_inferior().architecture().integer_type(0).function()" \
+		       "Create type of new symbol" 1
+gdb_py_test_silent_cmd "python sym = gdb.Symbol(\"static_block\", symtab, typ, gdb.SYMBOL_FUNCTION_DOMAIN, gdb.SYMBOL_LOC_BLOCK, cu.static_block() )" \
+		       "Create new symbol" 1
+gdb_test "python print ( sym in list(cu.global_block()) )" \
+	 "False" \
+	 "Symbol is not in global block"
+gdb_py_test_silent_cmd "python cu.global_block().add_symbol(sym)" \
+		       "Add new symbol to block" 1
+gdb_test "python print ( sym in list(cu.global_block()) )" \
+		       "True" \
+		       "Symbol is in global block"
+gdb_test "python print ( cu.global_block().add_symbol(cu))" \
+	 "TypeError.*:.*" \
+	 "Add non-symbol to block"
+
+
 # Test Block is_valid.  This must always be the last test in this
 # testcase as it unloads the object file.
 delete_breakpoints
-- 
2.45.2


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [RFC v2 18/21] gdb/python: add more attributes to gdb.LinetableEntry objects
  2024-11-21 12:46 [RFC v2 00/21] Add Python "JIT" API Jan Vrany
                   ` (16 preceding siblings ...)
  2024-11-21 12:47 ` [RFC v2 17/21] gdb/python: add add_symbol () method to gdb.Block Jan Vrany
@ 2024-11-21 12:47 ` Jan Vrany
  2024-11-21 13:28   ` Eli Zaretskii
  2024-11-21 12:47 ` [RFC v2 19/21] gdb/python: allow instantiation of gdb.LineTableEntry objects Jan Vrany
                   ` (2 subsequent siblings)
  20 siblings, 1 reply; 39+ messages in thread
From: Jan Vrany @ 2024-11-21 12:47 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany, Eli Zaretskii

This commit adds is_stmt, prologue_end and epilogue_begin attributes
to linetable entry objects.

This prompted change in gdb.Linetable.line() (ltpy_get_pcs_for_line).
In order to fill initialize new attributes we need complete entries
matching the line, not only PCs.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
 gdb/doc/python.texi                       |  17 ++++
 gdb/python/py-linetable.c                 | 101 +++++++++++++++++++---
 gdb/testsuite/gdb.python/py-linetable.exp |  10 +++
 3 files changed, 116 insertions(+), 12 deletions(-)

diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 43109dc6554..9fe7e182bdb 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -6674,6 +6674,23 @@ executable code for that source line resides in memory.  This
 attribute is not writable.
 @end defvar
 
+@defvar LineTableEntry.is_stmt
+True if pc (associated with this entry) is a good location to place
+a breakpoint for line (associated with this entry).  This attribute is not
+writable.
+@end defvar
+
+@defvar LineTableEntry.prologue_end
+True if pc (associated with this entry) is a good location to place
+a breakpoint after a function prologue.  This attribute is not
+writable.
+@end defvar
+
+@defvar LineTableEntry.epilogue_begin
+True if pc (associated with this entry) marks the start of the epilogue.
+This attribute is not writable.
+@end defvar
+
 As there can be multiple addresses for a single source line, you may
 receive multiple @code{LineTableEntry} objects with matching
 @code{line} attributes, but with different @code{pc} attributes.  The
diff --git a/gdb/python/py-linetable.c b/gdb/python/py-linetable.c
index fc57f369606..8b6806a2484 100644
--- a/gdb/python/py-linetable.c
+++ b/gdb/python/py-linetable.c
@@ -25,6 +25,12 @@ struct linetable_entry_object {
   int line;
   /* The pc associated with the source line.  */
   CORE_ADDR pc;
+  /* See is_stmt in stuct linetable_entry.  */
+  bool is_stmt : 1;
+  /* See prologue_end in stuct linetable_entry.  */
+  bool prologue_end : 1;
+  /* See epilogue_begin in struct linetable_entry.  */
+  bool epilogue_begin : 1;
 };
 
 extern PyTypeObject linetable_entry_object_type
@@ -98,7 +104,8 @@ symtab_to_linetable_object (PyObject *symtab)
    and an address.  */
 
 static PyObject *
-build_linetable_entry (int line, CORE_ADDR address)
+build_linetable_entry (int line, CORE_ADDR address, bool is_stmt,
+		       bool prologue_end, bool epilogue_begin)
 {
   linetable_entry_object *obj;
 
@@ -108,6 +115,9 @@ build_linetable_entry (int line, CORE_ADDR address)
     {
       obj->line = line;
       obj->pc = address;
+      obj->is_stmt = is_stmt;
+      obj->prologue_end = prologue_end;
+      obj->epilogue_begin = epilogue_begin;
     }
 
   return (PyObject *) obj;
@@ -120,22 +130,26 @@ build_linetable_entry (int line, CORE_ADDR address)
    address.  */
 
 static PyObject *
-build_line_table_tuple_from_pcs (int line, const std::vector<CORE_ADDR> &pcs)
+build_line_table_tuple_from_entries (
+	const struct objfile *objfile,
+	const std::vector<const linetable_entry *> &entries)
 {
   int i;
 
-  if (pcs.size () < 1)
+  if (entries.size () < 1)
     Py_RETURN_NONE;
 
-  gdbpy_ref<> tuple (PyTuple_New (pcs.size ()));
+  gdbpy_ref<> tuple (PyTuple_New (entries.size ()));
 
   if (tuple == NULL)
     return NULL;
 
-  for (i = 0; i < pcs.size (); ++i)
+  for (i = 0; i < entries.size (); ++i)
     {
-      CORE_ADDR pc = pcs[i];
-      gdbpy_ref<> obj (build_linetable_entry (line, pc));
+      auto entry = entries[i];
+      gdbpy_ref<> obj (build_linetable_entry (
+			entry->line, entry->pc (objfile), entry->is_stmt,
+			entry->prologue_end, entry->epilogue_begin));
 
       if (obj == NULL)
 	return NULL;
@@ -155,24 +169,35 @@ ltpy_get_pcs_for_line (PyObject *self, PyObject *args)
 {
   struct symtab *symtab;
   gdb_py_longest py_line;
-  const linetable_entry *best_entry = nullptr;
-  std::vector<CORE_ADDR> pcs;
+  std::vector<const linetable_entry*> entries;
 
   LTPY_REQUIRE_VALID (self, symtab);
 
   if (! PyArg_ParseTuple (args, GDB_PY_LL_ARG, &py_line))
     return NULL;
 
+  if (! symtab->linetable ())
+    Py_RETURN_NONE;
+
   try
     {
-      pcs = find_pcs_for_symtab_line (symtab, py_line, &best_entry);
+      const linetable_entry *entry;
+      int i;
+      for (entry = symtab->linetable ()->item, i = 0;
+	   i < symtab->linetable ()->nitems;
+	   entry++, i++)
+	{
+	  if (entry->line == py_line)
+	    entries.push_back (entry);
+	}
     }
   catch (const gdb_exception &except)
     {
       return gdbpy_handle_gdb_exception (nullptr, except);
     }
 
-  return build_line_table_tuple_from_pcs (py_line, pcs);
+  struct objfile *objfile = symtab->compunit ()->objfile ();
+  return build_line_table_tuple_from_entries (objfile, entries);
 }
 
 /* Implementation of gdb.LineTable.has_line (self, line) -> Boolean.
@@ -321,6 +346,50 @@ ltpy_entry_get_pc (PyObject *self, void *closure)
   return gdb_py_object_from_ulongest (obj->pc).release ();
 }
 
+/* Implementation of gdb.LineTableEntry.is_stmt (self) -> bool.  Returns
+   True if associated PC is a good location to place a breakpoint for
+   associatated LINE.  */
+
+static PyObject *
+ltpy_entry_get_is_stmt (PyObject *self, void *closure)
+{
+  linetable_entry_object *obj = (linetable_entry_object *) self;
+
+  if (obj->is_stmt != 0)
+    Py_RETURN_TRUE;
+  else
+    Py_RETURN_FALSE;
+}
+
+/* Implementation of gdb.LineTableEntry.prologue_end (self) -> bool.  Returns
+   True if associated PC is a good location to place a breakpoint after a
+   function prologue.  */
+
+static PyObject *
+ltpy_entry_get_prologue_end (PyObject *self, void *closure)
+{
+  linetable_entry_object *obj = (linetable_entry_object *) self;
+
+  if (obj->prologue_end)
+    Py_RETURN_TRUE;
+  else
+    Py_RETURN_FALSE;
+}
+
+/* Implementation of gdb.LineTableEntry.prologue_end (self) -> bool.  Returns
+   True if this location marks the start of the epilogue.  */
+
+static PyObject *
+ltpy_entry_get_epilogue_begin (PyObject *self, void *closure)
+{
+  linetable_entry_object *obj = (linetable_entry_object *) self;
+
+  if (obj->epilogue_begin)
+    Py_RETURN_TRUE;
+  else
+    Py_RETURN_FALSE;
+}
+
 /* LineTable iterator functions.  */
 
 /* Return a new line table iterator.  */
@@ -406,7 +475,8 @@ ltpy_iternext (PyObject *self)
     }
 
   struct objfile *objfile = symtab->compunit ()->objfile ();
-  obj = build_linetable_entry (item->line, item->pc (objfile));
+  obj = build_linetable_entry (item->line, item->pc (objfile), item->is_stmt,
+			       item->prologue_end, item->epilogue_begin );
   iter_obj->current_index++;
 
   return obj;
@@ -534,9 +604,16 @@ static gdb_PyGetSetDef linetable_entry_object_getset[] = {
     "The line number in the source file.", NULL },
   { "pc", ltpy_entry_get_pc, NULL,
     "The memory address for this line number.", NULL },
+  { "is_stmt", ltpy_entry_get_is_stmt, NULL,
+    "Whether this is a good location to place a breakpoint for associated LINE.", NULL },
+  { "prologue_end", ltpy_entry_get_prologue_end, NULL,
+    "Whether this is a good location to place a breakpoint after method prologue.", NULL },
+  { "epilogue_begin", ltpy_entry_get_epilogue_begin, NULL,
+    "True if this location marks the start of the epilogue.", NULL },
   { NULL }  /* Sentinel */
 };
 
+
 PyTypeObject linetable_entry_object_type = {
   PyVarObject_HEAD_INIT (NULL, 0)
   "gdb.LineTableEntry",	          /*tp_name*/
diff --git a/gdb/testsuite/gdb.python/py-linetable.exp b/gdb/testsuite/gdb.python/py-linetable.exp
index 5caa7c445c4..0f591e1ff99 100644
--- a/gdb/testsuite/gdb.python/py-linetable.exp
+++ b/gdb/testsuite/gdb.python/py-linetable.exp
@@ -53,6 +53,16 @@ gdb_test "python print(len(lt.line(20)))" "1" \
     "Test length of a single pc line"
 gdb_test "python print(lt.line(1))" "None" \
     "Test None returned for line with no pc"
+gdb_test "python print(next(iter(lt)).is_stmt)" \
+	 "(True|False)" \
+	 "Test is_stmt"
+gdb_test "python print(next(iter(lt)).prologue_end)" \
+	 "(True|False)" \
+	 "Test prologue_end"
+gdb_test "python print(next(iter(lt)).epilogue_begin)" \
+	 "(True|False)" \
+	 "Test epilogue_begin"
+
 
 # Test gdb.Linetable.sourcelines ()
 gdb_py_test_silent_cmd "python fset = lt.source_lines()" \
-- 
2.45.2


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [RFC v2 19/21] gdb/python: allow instantiation of gdb.LineTableEntry objects
  2024-11-21 12:46 [RFC v2 00/21] Add Python "JIT" API Jan Vrany
                   ` (17 preceding siblings ...)
  2024-11-21 12:47 ` [RFC v2 18/21] gdb/python: add more attributes to gdb.LinetableEntry objects Jan Vrany
@ 2024-11-21 12:47 ` Jan Vrany
  2024-11-21 13:27   ` Eli Zaretskii
  2024-11-21 12:47 ` [RFC v2 20/21] gdb/python: allow instantiation of gdb.LineTable objects Jan Vrany
  2024-11-21 12:47 ` [RFC v2 21/21] gdb/python: add section in documentation on implementing JIT interface Jan Vrany
  20 siblings, 1 reply; 39+ messages in thread
From: Jan Vrany @ 2024-11-21 12:47 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany, Eli Zaretskii

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
 gdb/doc/python.texi                       |  8 ++++
 gdb/python/py-linetable.c                 | 52 +++++++++++++++++++----
 gdb/testsuite/gdb.python/py-linetable.exp | 25 +++++++++++
 3 files changed, 77 insertions(+), 8 deletions(-)

diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 9fe7e182bdb..277d26c6376 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -6691,6 +6691,14 @@ True if pc (associated with this entry) marks the start of the epilogue.
 This attribute is not writable.
 @end defvar
 
+@code{LineTableEntry} objects have the following methods:
+
+@defun LineTableEntry.__init__ (line, pc@r{[}, is_stmt@r{][}, prologue_end@r{][}, epilogue_begin@r{]})
+Create new line table entry. Arguments correspond to @code{LineTableEntry}
+attributes described above.  Optional arguments @var{is_stmt},
+@var{prologue_end} and @var{epilogue_begin} default to @code{False}.
+@end defun
+
 As there can be multiple addresses for a single source line, you may
 receive multiple @code{LineTableEntry} objects with matching
 @code{line} attributes, but with different @code{pc} attributes.  The
diff --git a/gdb/python/py-linetable.c b/gdb/python/py-linetable.c
index 8b6806a2484..83a32389132 100644
--- a/gdb/python/py-linetable.c
+++ b/gdb/python/py-linetable.c
@@ -146,7 +146,7 @@ build_line_table_tuple_from_entries (
 
   for (i = 0; i < entries.size (); ++i)
     {
-      auto entry = entries[i];
+      const linetable_entry *entry = entries[i];
       gdbpy_ref<> obj (build_linetable_entry (
 			entry->line, entry->pc (objfile), entry->is_stmt,
 			entry->prologue_end, entry->epilogue_begin));
@@ -390,6 +390,41 @@ ltpy_entry_get_epilogue_begin (PyObject *self, void *closure)
     Py_RETURN_FALSE;
 }
 
+/* Object initializer; creates new linetable entry.
+
+   Use: __init__(LINE, PC, IS_STMT, PROLOGUE_END, EPILOGUE_BEGIN).  */
+
+static int
+ltpy_entry_init (PyObject *zelf, PyObject *args, PyObject *kw)
+{
+  linetable_entry_object *self = (linetable_entry_object *) zelf;
+
+   static const char *keywords[] = { "line", "pc", "is_stmt", "prologue_end",
+				     "epilogue_begin", nullptr };
+   int line = 0;
+   CORE_ADDR pc = 0;
+   int is_stmt = 0;
+   int prologue_end = 0;
+   int epilogue_begin = 0;
+
+   if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "iK|ppp",
+	  keywords,
+	  &line,
+	  &pc,
+	  &is_stmt,
+	  &prologue_end,
+	  &epilogue_begin))
+    return -1;
+
+   self->line = line;
+   self->pc = pc;
+   self->is_stmt = is_stmt == 1 ? true : false;
+   self->prologue_end = prologue_end == 1 ? true : false;
+   self->epilogue_begin = epilogue_begin == 1 ? true : false;
+
+   return 0;
+}
+
 /* LineTable iterator functions.  */
 
 /* Return a new line table iterator.  */
@@ -604,12 +639,12 @@ static gdb_PyGetSetDef linetable_entry_object_getset[] = {
     "The line number in the source file.", NULL },
   { "pc", ltpy_entry_get_pc, NULL,
     "The memory address for this line number.", NULL },
-  { "is_stmt", ltpy_entry_get_is_stmt, NULL,
-    "Whether this is a good location to place a breakpoint for associated LINE.", NULL },
-  { "prologue_end", ltpy_entry_get_prologue_end, NULL,
-    "Whether this is a good location to place a breakpoint after method prologue.", NULL },
-  { "epilogue_begin", ltpy_entry_get_epilogue_begin, NULL,
-    "True if this location marks the start of the epilogue.", NULL },
+  { "is_stmt", ltpy_entry_get_is_stmt, nullptr,
+    "Whether this is a good location to place a breakpoint for associated LINE.", nullptr },
+  { "prologue_end", ltpy_entry_get_prologue_end, nullptr,
+    "Whether this is a good location to place a breakpoint after method prologue.", nullptr },
+  { "epilogue_begin", ltpy_entry_get_epilogue_begin, nullptr,
+    "True if this location marks the start of the epilogue.", nullptr },
   { NULL }  /* Sentinel */
 };
 
@@ -650,6 +685,7 @@ PyTypeObject linetable_entry_object_type = {
   0,				  /* tp_descr_get */
   0,				  /* tp_descr_set */
   0,				  /* tp_dictoffset */
-  0,	                          /* tp_init */
+  ltpy_entry_init,		  /* tp_init */
   0,				  /* tp_alloc */
+  PyType_GenericNew,		  /* tp_new */
 };
diff --git a/gdb/testsuite/gdb.python/py-linetable.exp b/gdb/testsuite/gdb.python/py-linetable.exp
index 0f591e1ff99..e14a3bc1a0d 100644
--- a/gdb/testsuite/gdb.python/py-linetable.exp
+++ b/gdb/testsuite/gdb.python/py-linetable.exp
@@ -81,3 +81,28 @@ gdb_test "python print(lt.has_line(44))" \
 gdb_test "python print(lt.has_line(10))" \
     "False.*" \
     "test has_pcs at line 10"
+
+# Test gdb.LineTableEntry.__init__ ()
+gdb_test "python print( gdb.LineTableEntry(10, 0xcafe0000).line)" \
+    "10" \
+    "test new LineTableEntry line"
+
+gdb_test "python print( gdb.LineTableEntry(10, 123456).pc)" \
+    "123456" \
+    "test new LineTableEntry pc"
+
+gdb_test "python print( gdb.LineTableEntry(10, 123456).is_stmt)" \
+    "False" \
+    "test new LineTableEntry is_stmt"
+gdb_test "python print( gdb.LineTableEntry(10, 123456).prologue_end)" \
+    "False" \
+    "test new LineTableEntry prologue_end"
+gdb_test "python print( gdb.LineTableEntry(10, 123456).epilogue_begin)" \
+    "False" \
+    "test new LineTableEntry epilogue_begin"
+gdb_test "python print( gdb.LineTableEntry('xx', 123456).pc)" \
+    "TypeError.*:.*" \
+    "test creating invalid gdb.LineTableEntry"
+gdb_test "python print( gdb.LineTableEntry(10, 123456, prologue_end = True).prologue_end)" \
+    "True" \
+    "test prologue_end keyword argument"
-- 
2.45.2


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [RFC v2 20/21] gdb/python: allow instantiation of gdb.LineTable objects
  2024-11-21 12:46 [RFC v2 00/21] Add Python "JIT" API Jan Vrany
                   ` (18 preceding siblings ...)
  2024-11-21 12:47 ` [RFC v2 19/21] gdb/python: allow instantiation of gdb.LineTableEntry objects Jan Vrany
@ 2024-11-21 12:47 ` Jan Vrany
  2024-11-21 13:29   ` Eli Zaretskii
  2024-11-21 12:47 ` [RFC v2 21/21] gdb/python: add section in documentation on implementing JIT interface Jan Vrany
  20 siblings, 1 reply; 39+ messages in thread
From: Jan Vrany @ 2024-11-21 12:47 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany, Eli Zaretskii

This commit allows users to instantiate gdb.LineTable objects.
This is a step towards a Python support for dynamically generated code
(JIT) in GDB.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
 gdb/doc/python.texi                       |   8 ++
 gdb/python/py-linetable.c                 | 104 +++++++++++++++++++++-
 gdb/testsuite/gdb.python/py-linetable.exp |  32 ++++++-
 3 files changed, 140 insertions(+), 4 deletions(-)

diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 277d26c6376..2ab880f8d73 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -6728,6 +6728,14 @@ Line: 45 Address: 0x400615L
 In addition to being able to iterate over a @code{LineTable}, it also
 has the following direct access methods:
 
+@defun LineTable.__init__ (symtab, entries)
+Creates a new @code{LineTable} object and associate it with given
+@var{symtab}.  Old linetable that might already be associated with @var{symtab}
+is discarded.  The @var{entries} argument is a list of @code{LineTableEntry}
+objects that constitute the newly created line table.  Line table entries
+do not need to be sorted.
+@end defun
+
 @defun LineTable.line (line)
 Return a Python @code{Tuple} of @code{LineTableEntry} objects for any
 entries in the line table for the given @var{line}, which specifies
diff --git a/gdb/python/py-linetable.c b/gdb/python/py-linetable.c
index 83a32389132..4e93feabee8 100644
--- a/gdb/python/py-linetable.c
+++ b/gdb/python/py-linetable.c
@@ -17,7 +17,9 @@
    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 <algorithm>
 #include "python-internal.h"
+#include "objfiles.h"
 
 struct linetable_entry_object {
   PyObject_HEAD
@@ -294,6 +296,102 @@ ltpy_is_valid (PyObject *self, PyObject *args)
   Py_RETURN_TRUE;
 }
 
+/* Object initializer; creates new linetable.
+
+   Use: __init__(SYMTAB, ENTRIES).  */
+
+static int
+ltpy_init (PyObject *zelf, PyObject *args, PyObject *kw)
+{
+  struct linetable_object *self = (struct linetable_object*) zelf;
+
+  static const char *keywords[] = { "symtab", "entries", nullptr };
+  PyObject *symtab_obj;
+  PyObject *entries;
+
+  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "OO", keywords,
+					&symtab_obj, &entries))
+    return -1;
+
+  struct symtab *symtab = symtab_object_to_symtab (symtab_obj);
+  if (symtab == nullptr)
+    {
+      PyErr_Format (PyExc_TypeError,
+		    _("The symtab argument is not valid gdb.Symtab."));
+      return -1;
+    }
+
+  if (!PyList_Check (entries))
+    {
+      PyErr_Format (PyExc_TypeError,
+		    _("The entries parameter is not a list."));
+      return -1;
+    }
+
+  struct objfile *objfile = symtab->compunit ()->objfile ();
+
+  /* Commit 1acc9dca "Change linetables to be objfile-independent"
+     changed linetables so that entries contain relative of objfile's
+     text section offset.  Since the objfile has been created dynamically
+     and may not have "text" section offset initialized, we do it here.
+
+     Note that here no section is added to objfile (since that requires
+     having bfd_section first), only text offset.  */
+  if (objfile->sect_index_text == -1)
+    {
+      objfile->section_offsets.push_back (0);
+      objfile->sect_index_text = objfile->section_offsets.size () - 1;
+    }
+  CORE_ADDR text_section_offset = objfile->text_section_offset ();
+
+  long nentries = PyList_Size (entries);
+  long linetable_size
+    = sizeof (struct linetable)
+      + std::max(nentries - 1, 0L) * sizeof (struct linetable_entry);
+  struct linetable *linetable
+    = (struct linetable *)obstack_alloc (&(objfile->objfile_obstack),
+					 linetable_size);
+  linetable->nitems = nentries;
+  for (int i = 0; i < nentries; i++)
+    {
+      linetable_entry_object *entry_obj
+	= (linetable_entry_object *)PyList_GetItem (entries, i);
+			;
+      if (! PyObject_TypeCheck (entry_obj , &linetable_entry_object_type))
+	{
+	  PyErr_Format (PyExc_TypeError,
+		       _("Element at %d of entries argument is not a "
+			 "gdb.LineTableEntry object"), i);
+	  return -1;
+	}
+
+      /* Since PC of entries passed to this function are "unrelocated",
+	 we compensate here.  */
+      CORE_ADDR pc ((CORE_ADDR)entry_obj->pc - text_section_offset);
+
+      linetable->item[i].line = entry_obj->line;
+      linetable->item[i].set_unrelocated_pc (unrelocated_addr (pc));
+      linetable->item[i].is_stmt = entry_obj->is_stmt;
+      linetable->item[i].prologue_end = entry_obj->prologue_end;
+      linetable->item[i].epilogue_begin = entry_obj->epilogue_begin;
+    }
+  /* Now sort the entries in increasing PC order.  */
+  if (nentries > 0)
+    {
+      auto linetable_entry_ordering = [] (const struct linetable_entry &e1,
+					  const struct linetable_entry &e2)
+	{
+	  return e1.unrelocated_pc () < e2.unrelocated_pc ();
+	};
+      std::sort (&(linetable->item[0]), &(linetable->item[nentries]),
+		 linetable_entry_ordering);
+    }
+  symtab->set_linetable (linetable);
+  self->symtab = symtab_obj;
+  Py_INCREF (symtab_obj);
+
+  return 0;
+}
 /* Deconstructor for the line table object.  Decrement the reference
    to the symbol table object before calling the default free.  */
 
@@ -302,7 +400,8 @@ ltpy_dealloc (PyObject *self)
 {
   linetable_object *obj = (linetable_object *) self;
 
-  Py_DECREF (obj->symtab);
+  if (obj->symtab)
+    Py_DECREF (obj->symtab);
   Py_TYPE (self)->tp_free (self);
 }
 
@@ -591,8 +690,9 @@ PyTypeObject linetable_object_type = {
   0,				  /* tp_descr_get */
   0,				  /* tp_descr_set */
   0,				  /* tp_dictoffset */
-  0,    			  /* tp_init */
+  ltpy_init,   			  /* tp_init */
   0,				  /* tp_alloc */
+  PyType_GenericNew		  /* tp_new */
 };
 
 static PyMethodDef ltpy_iterator_methods[] = {
diff --git a/gdb/testsuite/gdb.python/py-linetable.exp b/gdb/testsuite/gdb.python/py-linetable.exp
index e14a3bc1a0d..1c94a0b97c9 100644
--- a/gdb/testsuite/gdb.python/py-linetable.exp
+++ b/gdb/testsuite/gdb.python/py-linetable.exp
@@ -39,12 +39,12 @@ gdb_py_test_silent_cmd "python lt = gdb.selected_frame().find_sal().symtab.linet
 
 gdb_test_multiline "input simple command" \
   "python" "" \
-  "def list_lines():" "" \
+  "def list_lines(lt):" "" \
   "   for l in lt:" "" \
   "     print ('L' + str(l.line) + ' A ' + hex(l.pc))" "" \
   "end" ""
 
-gdb_test "python list_lines()" \
+gdb_test "python list_lines(lt)" \
     "L20 A $hex.*L21 A $hex.*L22 A $hex.*L24 A $hex.*L25 A $hex.*L40 A $hex.*L42 A $hex.*L44 A $hex.*L42 A $hex.*L46 A $hex.*" \
     "test linetable iterator addr"
 gdb_test "python print(len(lt.line(42)))" "2" \
@@ -106,3 +106,31 @@ gdb_test "python print( gdb.LineTableEntry('xx', 123456).pc)" \
 gdb_test "python print( gdb.LineTableEntry(10, 123456, prologue_end = True).prologue_end)" \
     "True" \
     "test prologue_end keyword argument"
+
+# Test gdb.LineTable.__init__(). To do so, we create new compunit and new
+# symtab.
+gdb_py_test_silent_cmd "python cu = gdb.Compunit(\"dynamic_cu\", gdb.Objfile(\"dynamic_objf\"), 0x100, 0x200)" \
+    "create new compunit" 1
+gdb_py_test_silent_cmd "python st = gdb.Symtab(\"dynamic_st\", cu)" \
+    "create new symtab" 1
+gdb_test "python lt2 = gdb.LineTable(st, \[gdb.LineTableEntry(10, 0x123)\]); print(lt)" \
+    "<gdb.LineTable object at $hex.*" \
+    "create new linetable"
+gdb_test "python print(lt2.is_valid())" \
+    "True" \
+    "test new linetable is valid"
+gdb_test "python list_lines(lt2)" \
+    "L10 A 0x123.*" \
+    "test new linetable iterator"
+gdb_test "python print(len(list(gdb.LineTable(st, \[\]))))" \
+    "0" \
+    "test creating empty linetable"
+gdb_test "python gdb.LineTable(123, \[gdb.LineTableEntry(10, 0x123)\])" \
+    "TypeError.*:.*" \
+    "test creating linetable with invalid symtab"
+gdb_test "python gdb.LineTable(st, gdb.LineTableEntry(10, 0x123))" \
+    "TypeError.*:.*" \
+    "test creating linetable with invalid entries"
+gdb_test "python gdb.LineTable(st, \[gdb.LineTableEntry(10, 0x123), \"xxx\"\])" \
+    "TypeError.*:.*" \
+    "test creating linetable with invalid element in entries"
-- 
2.45.2


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [RFC v2 21/21] gdb/python: add section in documentation on implementing JIT interface
  2024-11-21 12:46 [RFC v2 00/21] Add Python "JIT" API Jan Vrany
                   ` (19 preceding siblings ...)
  2024-11-21 12:47 ` [RFC v2 20/21] gdb/python: allow instantiation of gdb.LineTable objects Jan Vrany
@ 2024-11-21 12:47 ` Jan Vrany
  2024-11-21 13:31   ` Eli Zaretskii
  20 siblings, 1 reply; 39+ messages in thread
From: Jan Vrany @ 2024-11-21 12:47 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany, Eli Zaretskii

This commit adds new section - JIT Interface in Python - outlining how
to use Python APIs introduced in previous commits to implement simple
JIT interface. It also adds new test to make sure the example code is
up-to-date.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
 gdb/NEWS                            |   4 +
 gdb/doc/gdb.texinfo                 |   3 +-
 gdb/doc/python.texi                 | 122 ++++++++++++++++++++++++++++
 gdb/testsuite/gdb.python/py-jit.c   |  61 ++++++++++++++
 gdb/testsuite/gdb.python/py-jit.exp |  57 +++++++++++++
 gdb/testsuite/gdb.python/py-jit.py  | 110 +++++++++++++++++++++++++
 6 files changed, 356 insertions(+), 1 deletion(-)
 create mode 100644 gdb/testsuite/gdb.python/py-jit.c
 create mode 100644 gdb/testsuite/gdb.python/py-jit.exp
 create mode 100644 gdb/testsuite/gdb.python/py-jit.py

diff --git a/gdb/NEWS b/gdb/NEWS
index 7273b23f989..ab3463d9893 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -96,6 +96,10 @@
 
   ** Added class gdb.Compunit.
 
+  ** Extended the Python API to allow interfacing with JIT compilers using
+     Python (as an alternative to JIT reader API).  For details, see Section
+     "JIT Interface in Python" in GDB documentation.
+
 * Debugger Adapter Protocol changes
 
   ** The "scopes" request will now return a scope holding global
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 99720f1206e..b292f6c8a89 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -39886,7 +39886,8 @@ If you are using @value{GDBN} to debug a program that uses this interface, then
 it should work transparently so long as you have not stripped the binary.  If
 you are developing a JIT compiler, then the interface is documented in the rest
 of this chapter.  At this time, the only known client of this interface is the
-LLVM JIT.
+LLVM JIT.  An alternative to interface descrived below is to implement JIT
+interface in Python (@pxref{JIT Interface in Python}).
 
 Broadly speaking, the JIT interface mirrors the dynamic loader interface.  The
 JIT compiler communicates with @value{GDBN} by writing data into a global
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 2ab880f8d73..a67f5836b2f 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -233,6 +233,7 @@ optional arguments while skipping others.  Example:
 * Disassembly In Python::       Instruction Disassembly In Python
 * Missing Debug Info In Python:: Handle missing debug info from Python.
 * Missing Objfiles In Python::  Handle objfiles from Python.
+* JIT Interface in Python::     Writing JIT compilation interface in Python
 @end menu
 
 @node Basic Python
@@ -8554,6 +8555,127 @@ handlers, all of the matching handlers are enabled.  The
 @code{enabled} field of each matching handler is set to @code{True}.
 @end table
 
+@node JIT Interface in Python
+@subsubsection Writing JIT compilation interface in Python
+@cindex python, just-in-time compilation, JIT compilation interface
+
+This section provides a high-level overview how to implement a JIT compiler
+interface entirely in Python.  For alternative way of interfacing a JIT
+@pxref{JIT Interface}.
+
+A JIT compiler interface usually needs to implement three elements:
+
+@enumerate
+@item
+A way how to get notified when the JIT compiler compiles (and installs) new
+code and when existing code is discarded.  Typical solution is to put a Python
+breakpoint (@pxref{Breakpoints In Python}) on some function known to be
+called by the JIT compiler once code is installed or discarded.
+
+@item
+When a new code is installed the JIT interface needs to extract (debug)
+information for newly installed code from the JIT compiler
+(@pxref{Values From Inferior}) and build @value{GDBN}'s internal structures.
+@xref{Objfiles In Python}, @ref{Compunits In Python},
+ @ref{Blocks In Python},   @ref{Symbol Tables In Python},
+ @ref{Symbols In Python},  @ref{Line Tables In Python}).
+
+@item
+Finally, when (previously installed) code is discarded the JIT interface
+needs to discard @value{GDBN}'s internal structures built in previous step.
+This is done by calling @code{unlink} on an objfile for that code
+(which was created in previous step).
+@end enumerate
+
+Here's an example showing how to write a simple JIT interface in Python:
+
+@c The copy of the code below is also in testsuite/gdb.python/py-jit.py
+@c and used by py-jit.exp to make sure it is up to date.  If changed the
+@c test and py-jit.py should be checked and update accordingly if needed.
+@smallexample
+import gdb
+
+class JITRegisterCode(gdb.Breakpoint):
+    def stop(self):
+	# Extract new code's address, size, name, linetable (and possibly
+	# other useful information).  How exactly to do so depends on JIT
+	# compiler in question.
+	#
+	# In this example address, size and name get passed as parameters
+	# to registration function.
+
+	frame = gdb.newest_frame()
+	addr = int(frame.read_var('code'))
+	size = int(frame.read_var('size'))
+	name = frame.read_var('name').string()
+	linetable_entries = get_linetable_entries(addr)
+
+	# Create objfile and compunit for allocated "jitted" code
+	objfile = gdb.Objfile(name)
+	compunit = gdb.Compunit(name, objfile, addr, addr + size)
+
+	# Mark the objfile as "jitted" code.  This will be used later when
+	# unregistering discarded code to check the objfile was indeed
+	# created for "jitted" code.
+	setattr(objfile, "is_jit_code", True)
+
+	# Create block for jitted function
+	block = gdb.Block(compunit.static_block(), addr, addr + size)
+
+	# Create symbol table holding info about jitted function, ...
+	symtab = gdb.Symtab("py-jit.c", compunit)
+	linetable = gdb.LineTable(symtab, linetable_entries)
+
+	# ...type of the jitted function...
+	void_t = gdb.selected_inferior().architecture().void_type()
+	func_t = void_t.function(None)
+
+	# ...and symbol representing jitted function.
+	symbol = gdb.Symbol(name, symtab, func_t,
+			    gdb.SYMBOL_FUNCTION_DOMAIN, gdb.SYMBOL_LOC_BLOCK,
+			    block)
+
+	# Finally, register the symbol in static block...
+	compunit.static_block().add_symbol(symbol)
+
+	# ..and continue execution
+	return False
+
+# Create breakpoint to register new code
+JITRegisterCode("jit_register_code", internal=True)
+
+
+class JITUnregisterCode(gdb.Breakpoint):
+    def stop(self):
+	# Find out which code has been discarded.  Again, how exactly to
+	# do so depends on JIT compiler in question.
+	#
+	# In this example address of discarded code is passed as a
+	# parameter.
+
+	frame = gdb.newest_frame()
+	addr = int(frame.read_var('code'))
+
+	# Find objfile which was created in JITRegisterCode.stop() for
+	# given jitted code.
+	objfile = gdb.current_progspace().objfile_for_address(addr)
+	if objfile is None:
+	    # No objfile for given addr (this should not normally happen)
+	    return False # Continue execution
+	if not getattr(objfile, "is_jit_code", False):
+	    # Not a jitted code (this should not happen either)
+	    return False # Continue execution
+
+	# Remove the objfile and all debug info associated with it...
+	objfile.unlink()
+
+	# ..and continue execution
+	return False # Continue execution
+
+# Create breakpoint to discard old code
+JITUnregisterCode("jit_unregister_code", internal=True)
+@end smallexample
+
 @node Python Auto-loading
 @subsection Python Auto-loading
 @cindex Python auto-loading
diff --git a/gdb/testsuite/gdb.python/py-jit.c b/gdb/testsuite/gdb.python/py-jit.c
new file mode 100644
index 00000000000..2d1a621e9c9
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-jit.c
@@ -0,0 +1,61 @@
+/* Copyright (C) 2024-2024 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 <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/mman.h>
+
+/* "JIT-ed" function, with the prototype `long (long, long)`.  */
+static const unsigned char jit_function_add_code[] = {
+  0x48, 0x01, 0xfe,		/* add %rdi,%rsi */
+  0x48, 0x89, 0xf0,		/* mov %rsi,%rax */
+  0xc3,				/* retq */
+};
+
+/* Dummy function to inform the debugger a new code has been installed.  */
+void jit_register_code (char * name, uintptr_t code, size_t size)
+{}
+
+/* Dummy function to inform the debugger that code has been installed.  */
+void jit_unregister_code (uintptr_t code)
+{}
+
+int
+main (int argc, char **argv)
+{
+  void *code = mmap (NULL, getpagesize (), PROT_WRITE | PROT_EXEC,
+		     MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  assert (code != MAP_FAILED);
+
+  /* "Compile" jit_function_add.  */
+  memcpy (code, jit_function_add_code,
+	  sizeof (jit_function_add_code));
+  jit_register_code ("jit_function_add", (uintptr_t)code, sizeof (jit_function_add_code));
+
+  ((long (*)(long, long))code)(1,5); /* breakpoint 1 line */
+
+  /* "Discard" jit_function_add.  */
+  memset(code, 0, sizeof(jit_function_add_code));
+  jit_unregister_code ((uintptr_t)code);
+
+  return 0; /* breakpoint 2 line */
+}
diff --git a/gdb/testsuite/gdb.python/py-jit.exp b/gdb/testsuite/gdb.python/py-jit.exp
new file mode 100644
index 00000000000..d2aa87a16ed
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-jit.exp
@@ -0,0 +1,57 @@
+# Copyright (C) 2024-2024 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 test the Python API to
+# create symbol tables for dynamic (JIT) code and follows the example
+# code given in documentation (see section JIT Interface in Python)
+
+load_lib gdb-python.exp
+
+require allow_python_tests
+require is_x86_64_m64_target
+
+standard_testfile
+
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return 0
+}
+
+set remote_python_file [gdb_remote_download host \
+                                ${srcdir}/${subdir}/${testfile}.py]
+gdb_test_no_output "source ${remote_python_file}" "load python file"
+
+gdb_breakpoint [gdb_get_line_number "breakpoint 1 line" ${testfile}.c]
+gdb_continue_to_breakpoint "continue to breakpoint 1 line"
+gdb_test "disassemble /s jit_function_add" \
+	"Dump of assembler code for function jit_function_add:.*End of assembler dump." \
+	"disassemble jit_function_add"
+
+gdb_breakpoint "jit_function_add"
+gdb_continue_to_breakpoint "continue to jit_function_add"
+
+gdb_test "bt 1" \
+	"#0  jit_function_add \\(\\) at py-jit.c:.*" \
+	"bt 1"
+
+gdb_breakpoint [gdb_get_line_number "breakpoint 2 line" ${testfile}.c]
+gdb_continue_to_breakpoint "continue to breakpoint 2 line"
+
+gdb_test "disassemble jit_function_add" \
+	"No symbol \"jit_function_add\" in current context." \
+	"disassemble jit_function_add after code has been unregistered"
diff --git a/gdb/testsuite/gdb.python/py-jit.py b/gdb/testsuite/gdb.python/py-jit.py
new file mode 100644
index 00000000000..b46b4ec9fe4
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-jit.py
@@ -0,0 +1,110 @@
+# Copyright (C) 2024-2024 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/>.  */
+
+# This code is same (modulo small tweaks) as the code in documentation,
+# section "JIT Interface in Python". If changed the documentation should
+# be checked and updated accordingly if necessary.
+import gdb
+
+objfile = None
+compunit = None
+block = None
+symtab = None
+symbol = None
+
+def get_linetable_entries(addr):
+    # Entries are not in increasing order to test that
+    # gdb.LineTable.__init__() sorts them properly.
+    return [
+	gdb.LineTableEntry(31, addr+6, True),
+	gdb.LineTableEntry(29, addr, True),
+	gdb.LineTableEntry(30, addr+3, True)
+    ]
+
+
+class JITRegisterCode(gdb.Breakpoint):
+    def stop(self):
+
+        global objfile 
+        global compunit
+        global block
+        global symtab
+        global symbol
+
+        frame = gdb.newest_frame()
+        name = frame.read_var('name').string()
+        addr = int(frame.read_var('code'))
+        size = int(frame.read_var('size'))
+        linetable_entries = get_linetable_entries(addr)
+
+        # Create objfile and compunit for allocated "jit" code
+        objfile = gdb.Objfile(name)
+        compunit = gdb.Compunit(name, objfile, addr, addr + size)
+
+        # Mark the objfile as "jitted code". This will be used later when
+        # unregistering discarded code to check the objfile was indeed
+        # created for jitted code.
+        setattr(objfile, "is_jit_code", True)
+
+        # Create block for jitted function
+        block = gdb.Block(compunit.static_block(), addr, addr + size)
+
+        # Create symbol table holding info about jitted function, ...
+        symtab = gdb.Symtab("py-jit.c", compunit)
+        linetable = gdb.LineTable(symtab, linetable_entries)
+
+        # ...type of the jitted function...
+        int64_t = gdb.selected_inferior().architecture().integer_type(64)
+        func_t = int64_t.function(int64_t, int64_t)
+
+        # ...and symbol representing jitted function.
+        symbol = gdb.Symbol(name, symtab, func_t,
+                                gdb.SYMBOL_FUNCTION_DOMAIN, gdb.SYMBOL_LOC_BLOCK,
+                                block)
+
+        # Finally, register the symbol in static block
+        compunit.static_block().add_symbol(symbol)
+
+        return False # Continue execution
+
+# Create breakpoint to register new code
+JITRegisterCode("jit_register_code", internal=True)
+
+
+class JITUnregisterCode(gdb.Breakpoint):
+    def stop(self):
+        frame = gdb.newest_frame()
+        addr = int(frame.read_var('code'))
+
+        objfile = gdb.current_progspace().objfile_for_address(addr)
+        if objfile is None:
+            # No objfile for given addr - bail out (this should not happen)
+            assert False
+            return False # Continue execution
+
+        if not getattr(objfile, "is_jit_code", False):
+            # Not a jitted addr - bail out (this should not happen either)
+            assert False
+            return False # Continue execution
+
+        # Remove the objfile and all debug info associated with it.
+        objfile.unlink()
+
+        return False # Continue execution
+
+# Create breakpoint to discard old code
+JITUnregisterCode("jit_unregister_code", internal=True)
-- 
2.45.2


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC v2 19/21] gdb/python: allow instantiation of gdb.LineTableEntry objects
  2024-11-21 12:47 ` [RFC v2 19/21] gdb/python: allow instantiation of gdb.LineTableEntry objects Jan Vrany
@ 2024-11-21 13:27   ` Eli Zaretskii
  0 siblings, 0 replies; 39+ messages in thread
From: Eli Zaretskii @ 2024-11-21 13:27 UTC (permalink / raw)
  To: Jan Vrany; +Cc: gdb-patches

> From: Jan Vrany <jan.vrany@labware.com>
> CC: Jan Vrany <jan.vrany@labware.com>,
> 	Eli Zaretskii <eliz@gnu.org>
> Date: Thu, 21 Nov 2024 12:47:12 +0000
> 
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> ---
>  gdb/doc/python.texi                       |  8 ++++
>  gdb/python/py-linetable.c                 | 52 +++++++++++++++++++----
>  gdb/testsuite/gdb.python/py-linetable.exp | 25 +++++++++++
>  3 files changed, 77 insertions(+), 8 deletions(-)

OK for the documentation part, thanks.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC v2 18/21] gdb/python: add more attributes to gdb.LinetableEntry objects
  2024-11-21 12:47 ` [RFC v2 18/21] gdb/python: add more attributes to gdb.LinetableEntry objects Jan Vrany
@ 2024-11-21 13:28   ` Eli Zaretskii
  0 siblings, 0 replies; 39+ messages in thread
From: Eli Zaretskii @ 2024-11-21 13:28 UTC (permalink / raw)
  To: Jan Vrany; +Cc: gdb-patches

> From: Jan Vrany <jan.vrany@labware.com>
> CC: Jan Vrany <jan.vrany@labware.com>,
> 	Eli Zaretskii <eliz@gnu.org>
> Date: Thu, 21 Nov 2024 12:47:11 +0000
> 
> This commit adds is_stmt, prologue_end and epilogue_begin attributes
> to linetable entry objects.
> 
> This prompted change in gdb.Linetable.line() (ltpy_get_pcs_for_line).
> In order to fill initialize new attributes we need complete entries
> matching the line, not only PCs.
> 
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> ---
>  gdb/doc/python.texi                       |  17 ++++
>  gdb/python/py-linetable.c                 | 101 +++++++++++++++++++---
>  gdb/testsuite/gdb.python/py-linetable.exp |  10 +++
>  3 files changed, 116 insertions(+), 12 deletions(-)

OK for the documentation part, thanks.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC v2 20/21] gdb/python: allow instantiation of gdb.LineTable objects
  2024-11-21 12:47 ` [RFC v2 20/21] gdb/python: allow instantiation of gdb.LineTable objects Jan Vrany
@ 2024-11-21 13:29   ` Eli Zaretskii
  0 siblings, 0 replies; 39+ messages in thread
From: Eli Zaretskii @ 2024-11-21 13:29 UTC (permalink / raw)
  To: Jan Vrany; +Cc: gdb-patches

> From: Jan Vrany <jan.vrany@labware.com>
> CC: Jan Vrany <jan.vrany@labware.com>,
> 	Eli Zaretskii <eliz@gnu.org>
> Date: Thu, 21 Nov 2024 12:47:13 +0000
> 
> This commit allows users to instantiate gdb.LineTable objects.
> This is a step towards a Python support for dynamically generated code
> (JIT) in GDB.
> 
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> ---
>  gdb/doc/python.texi                       |   8 ++
>  gdb/python/py-linetable.c                 | 104 +++++++++++++++++++++-
>  gdb/testsuite/gdb.python/py-linetable.exp |  32 ++++++-
>  3 files changed, 140 insertions(+), 4 deletions(-)

OK for the documentation part, thanks.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC v2 21/21] gdb/python: add section in documentation on implementing JIT interface
  2024-11-21 12:47 ` [RFC v2 21/21] gdb/python: add section in documentation on implementing JIT interface Jan Vrany
@ 2024-11-21 13:31   ` Eli Zaretskii
  0 siblings, 0 replies; 39+ messages in thread
From: Eli Zaretskii @ 2024-11-21 13:31 UTC (permalink / raw)
  To: Jan Vrany; +Cc: gdb-patches

> From: Jan Vrany <jan.vrany@labware.com>
> CC: Jan Vrany <jan.vrany@labware.com>,
> 	Eli Zaretskii <eliz@gnu.org>
> Date: Thu, 21 Nov 2024 12:47:14 +0000
> 
> This commit adds new section - JIT Interface in Python - outlining how
> to use Python APIs introduced in previous commits to implement simple
> JIT interface. It also adds new test to make sure the example code is
> up-to-date.
> 
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> ---
>  gdb/NEWS                            |   4 +
>  gdb/doc/gdb.texinfo                 |   3 +-
>  gdb/doc/python.texi                 | 122 ++++++++++++++++++++++++++++
>  gdb/testsuite/gdb.python/py-jit.c   |  61 ++++++++++++++
>  gdb/testsuite/gdb.python/py-jit.exp |  57 +++++++++++++
>  gdb/testsuite/gdb.python/py-jit.py  | 110 +++++++++++++++++++++++++
>  6 files changed, 356 insertions(+), 1 deletion(-)
>  create mode 100644 gdb/testsuite/gdb.python/py-jit.c
>  create mode 100644 gdb/testsuite/gdb.python/py-jit.exp
>  create mode 100644 gdb/testsuite/gdb.python/py-jit.py

OK for the documentation part, thanks.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC v2 04/21] gdb/python: add void_type () method to gdb.Architecture object
  2024-11-21 12:46 ` [RFC v2 04/21] gdb/python: add void_type () method to gdb.Architecture object Jan Vrany
@ 2024-11-21 13:32   ` Eli Zaretskii
  0 siblings, 0 replies; 39+ messages in thread
From: Eli Zaretskii @ 2024-11-21 13:32 UTC (permalink / raw)
  To: Jan Vrany; +Cc: gdb-patches

> From: Jan Vrany <jan.vrany@labware.com>
> CC: Jan Vrany <jan.vrany@labware.com>,
> 	Eli Zaretskii <eliz@gnu.org>
> Date: Thu, 21 Nov 2024 12:46:57 +0000
> 
> This commit adds a new method to Python architecture objects that
> returns a void type for that architecture.
> 
> This will be useful later to create types for function symbols created
> using Python extension code.
> 
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> ---
>  gdb/NEWS                             |  3 +++
>  gdb/doc/python.texi                  |  4 ++++
>  gdb/python/py-arch.c                 | 16 ++++++++++++++++
>  gdb/testsuite/gdb.python/py-arch.exp |  4 ++++
>  4 files changed, 27 insertions(+)

OK for the documentation part, thanks.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC v2 03/21] gdb/python: add domain property to gdb.Symbol
  2024-11-21 12:46 ` [RFC v2 03/21] gdb/python: add domain property to gdb.Symbol Jan Vrany
@ 2024-11-21 13:32   ` Eli Zaretskii
  0 siblings, 0 replies; 39+ messages in thread
From: Eli Zaretskii @ 2024-11-21 13:32 UTC (permalink / raw)
  To: Jan Vrany; +Cc: gdb-patches

> From: Jan Vrany <jan.vrany@labware.com>
> CC: Jan Vrany <jan.vrany@labware.com>,
> 	Eli Zaretskii <eliz@gnu.org>
> Date: Thu, 21 Nov 2024 12:46:56 +0000
> 
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> ---
>  gdb/NEWS                               |  2 ++
>  gdb/doc/python.texi                    |  5 +++++
>  gdb/python/py-symbol.c                 | 11 +++++++++++
>  gdb/testsuite/gdb.python/py-symbol.exp |  1 +
>  4 files changed, 19 insertions(+)

OK for the documentation part, thanks.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC v2 05/21] gdb/python: add function () method to gdb.Type object
  2024-11-21 12:46 ` [RFC v2 05/21] gdb/python: add function () method to gdb.Type object Jan Vrany
@ 2024-11-21 13:34   ` Eli Zaretskii
  2024-11-21 14:06     ` Jan Vraný
  0 siblings, 1 reply; 39+ messages in thread
From: Eli Zaretskii @ 2024-11-21 13:34 UTC (permalink / raw)
  To: Jan Vrany; +Cc: gdb-patches

> From: Jan Vrany <jan.vrany@labware.com>
> CC: Jan Vrany <jan.vrany@labware.com>,
> 	Eli Zaretskii <eliz@gnu.org>
> Date: Thu, 21 Nov 2024 12:46:58 +0000
> 
> This commit adds a new method to Python type objects that returns
> possibly new function type returning that type. Parameter types can
> be specified too.
> 
> This will be useful later to create types for function symbols created
> using Python extension code.
> 
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> ---
>  gdb/NEWS                             |  3 ++
>  gdb/doc/python.texi                  |  8 +++++
>  gdb/python/py-type.c                 | 54 ++++++++++++++++++++++++++++
>  gdb/testsuite/gdb.python/py-type.exp | 20 +++++++++++
>  4 files changed, 85 insertions(+)

The documentation parts are okay, but...

> +@defun Type.function (@r{[}param_type...@r{]})
> +Return a new @code{gdb.Type} object which represents a type of function
> +returning this type.  @code{param_type...} arguments specify parameter

...please use @dots{} instead of literal "..." in both cases here,
when you push.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC v2 02/21] gdb/python: add subblocks property to gdb.Block
  2024-11-21 12:46 ` [RFC v2 02/21] gdb/python: add subblocks property to gdb.Block Jan Vrany
@ 2024-11-21 13:35   ` Eli Zaretskii
  0 siblings, 0 replies; 39+ messages in thread
From: Eli Zaretskii @ 2024-11-21 13:35 UTC (permalink / raw)
  To: Jan Vrany; +Cc: gdb-patches

> From: Jan Vrany <jan.vrany@labware.com>
> CC: Jan Vrany <jan.vrany@labware.com>,
> 	Eli Zaretskii <eliz@gnu.org>
> Date: Thu, 21 Nov 2024 12:46:55 +0000
> 
> This commit adds new propery "subblocks" to gdb.Block objects. This
> allows Python to traverse block tree starting with global block.
> 
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> ---
>  gdb/NEWS                              |  3 +++
>  gdb/doc/python.texi                   |  5 +++++
>  gdb/python/py-block.c                 | 30 +++++++++++++++++++++++++++
>  gdb/testsuite/gdb.python/py-block.exp |  4 ++++
>  4 files changed, 42 insertions(+)

OK for the documentation part, thanks.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC v2 15/21] gdb/python: allow instantiation of gdb.Block from Python
  2024-11-21 12:47 ` [RFC v2 15/21] gdb/python: allow instantiation of gdb.Block " Jan Vrany
@ 2024-11-21 13:35   ` Eli Zaretskii
  0 siblings, 0 replies; 39+ messages in thread
From: Eli Zaretskii @ 2024-11-21 13:35 UTC (permalink / raw)
  To: Jan Vrany; +Cc: gdb-patches

> From: Jan Vrany <jan.vrany@labware.com>
> CC: Jan Vrany <jan.vrany@labware.com>,
> 	Eli Zaretskii <eliz@gnu.org>
> Date: Thu, 21 Nov 2024 12:47:08 +0000
> 
> 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 <eliz@gnu.org>
> ---
>  gdb/doc/python.texi                   |   6 ++
>  gdb/python/py-block.c                 | 111 +++++++++++++++++++++++++-
>  gdb/testsuite/gdb.python/py-block.exp |  23 ++++++
>  3 files changed, 138 insertions(+), 2 deletions(-)

OK for the documentation part, thanks.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC v2 17/21] gdb/python: add add_symbol () method to gdb.Block
  2024-11-21 12:47 ` [RFC v2 17/21] gdb/python: add add_symbol () method to gdb.Block Jan Vrany
@ 2024-11-21 13:36   ` Eli Zaretskii
  0 siblings, 0 replies; 39+ messages in thread
From: Eli Zaretskii @ 2024-11-21 13:36 UTC (permalink / raw)
  To: Jan Vrany; +Cc: gdb-patches

> From: Jan Vrany <jan.vrany@labware.com>
> CC: Jan Vrany <jan.vrany@labware.com>,
> 	Eli Zaretskii <eliz@gnu.org>
> Date: Thu, 21 Nov 2024 12:47:10 +0000
> 
> This commit adds new method add_symbol () to gdb.Block objects.
> A typical use of it is to add previously instantiated gdb.Symbol object
> to block when interfacing with JIT compiler.
> 
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> ---
>  gdb/doc/python.texi                   |  5 +++
>  gdb/python/py-block.c                 | 52 +++++++++++++++++++++++----
>  gdb/testsuite/gdb.python/py-block.exp | 20 +++++++++++
>  3 files changed, 71 insertions(+), 6 deletions(-)

OK for the documentation part, thanks.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC v2 16/21] gdb/python: allow instantiation of gdb.Symbol from Python
  2024-11-21 12:47 ` [RFC v2 16/21] gdb/python: allow instantiation of gdb.Symbol " Jan Vrany
@ 2024-11-21 13:37   ` Eli Zaretskii
  0 siblings, 0 replies; 39+ messages in thread
From: Eli Zaretskii @ 2024-11-21 13:37 UTC (permalink / raw)
  To: Jan Vrany; +Cc: gdb-patches

> From: Jan Vrany <jan.vrany@labware.com>
> CC: Jan Vrany <jan.vrany@labware.com>,
> 	Eli Zaretskii <eliz@gnu.org>
> Date: Thu, 21 Nov 2024 12:47:09 +0000
> 
> This commit adds code to allow user extension to instantiate
> gdb.Symbol.
> 
> As of now only "function" symbols can be created (that is: symbols
> of FUNCTION_DOMAIN and with address class LOC_BLOCK). This is enough
> to be able to implement "JIT reader" equivalent in Python. Future
> commits may extend this API to allow creation of other kinds of symbols
> (static variables, arguments, locals and so on).
> 
> Like previous similar commits, this is a step towards a Python support
> for dynamically generated code (JIT) in GDB.
> 
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> ---
>  gdb/doc/python.texi                    |  26 +++++
>  gdb/objfiles.c                         |  20 ++++
>  gdb/objfiles.h                         |   4 +
>  gdb/python/py-symbol.c                 | 139 ++++++++++++++++++++++++-
>  gdb/testsuite/gdb.python/py-symbol.exp |  13 +++
>  5 files changed, 201 insertions(+), 1 deletion(-)

OK for the documentation part, thanks.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC v2 10/21] gdb/python: add gdb.Compunit
  2024-11-21 12:47 ` [RFC v2 10/21] gdb/python: add gdb.Compunit Jan Vrany
@ 2024-11-21 13:39   ` Eli Zaretskii
  0 siblings, 0 replies; 39+ messages in thread
From: Eli Zaretskii @ 2024-11-21 13:39 UTC (permalink / raw)
  To: Jan Vrany; +Cc: gdb-patches

> From: Jan Vrany <jan.vrany@labware.com>
> CC: Jan Vrany <jan.vrany@labware.com>,
> 	Eli Zaretskii <eliz@gnu.org>
> Date: Thu, 21 Nov 2024 12:47:03 +0000
> 
> This commit introduces gdb.Compunit - a representation of struct
> compunit_symtab in Python.
> 
> It also adds method gdb.Objfile.compunits() to get a list of compunits
> for an objfile and adds compunit attribute to gdb.Block and gdb.Symtab
> to access compunit containing given block or symbol table.
> 
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> ---
>  gdb/Makefile.in                          |   1 +
>  gdb/NEWS                                 |   2 +
>  gdb/doc/python.texi                      |  69 +++++
>  gdb/python/py-block.c                    |  15 ++
>  gdb/python/py-compunit.c                 | 319 +++++++++++++++++++++++
>  gdb/python/py-objfile.c                  |  29 +++
>  gdb/python/py-symtab.c                   |  14 +
>  gdb/python/python-internal.h             |   3 +
>  gdb/testsuite/gdb.python/py-block.exp    |   2 +
>  gdb/testsuite/gdb.python/py-compunit.exp |  62 +++++
>  gdb/testsuite/gdb.python/py-symtab.exp   |   8 +
>  11 files changed, 524 insertions(+)
>  create mode 100644 gdb/python/py-compunit.c
>  create mode 100644 gdb/testsuite/gdb.python/py-compunit.exp

OK for the documentation part, thanks.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC v2 12/21] gdb/python: add unlink () method to gdb.Objfile object
  2024-11-21 12:47 ` [RFC v2 12/21] gdb/python: add unlink () method to gdb.Objfile object Jan Vrany
@ 2024-11-21 13:39   ` Eli Zaretskii
  0 siblings, 0 replies; 39+ messages in thread
From: Eli Zaretskii @ 2024-11-21 13:39 UTC (permalink / raw)
  To: Jan Vrany; +Cc: gdb-patches

> From: Jan Vrany <jan.vrany@labware.com>
> CC: Jan Vrany <jan.vrany@labware.com>,
> 	Eli Zaretskii <eliz@gnu.org>
> Date: Thu, 21 Nov 2024 12:47:05 +0000
> 
> This commit adds method allowing one remove any objfile. This is meant
> to be used to remove objfiles for dynamic code when this dynamic code
> is discarded. However gdb.Objfile.unlink() makes no attempt to ensure
> this - to make it consistent with other Python API to create and modify
> objfiles related structures (compunits, symbol tables and so on).
> 
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> ---
>  gdb/doc/python.texi                     |  6 ++++++
>  gdb/python/py-objfile.c                 | 18 ++++++++++++++++++
>  gdb/testsuite/gdb.python/py-objfile.exp | 12 ++++++++++++
>  3 files changed, 36 insertions(+)

OK for the documentation part, thanks.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC v2 13/21] gdb/python: allow instantiation of gdb.Compunit from Python
  2024-11-21 12:47 ` [RFC v2 13/21] gdb/python: allow instantiation of gdb.Compunit from Python Jan Vrany
@ 2024-11-21 13:40   ` Eli Zaretskii
  0 siblings, 0 replies; 39+ messages in thread
From: Eli Zaretskii @ 2024-11-21 13:40 UTC (permalink / raw)
  To: Jan Vrany; +Cc: gdb-patches

> From: Jan Vrany <jan.vrany@labware.com>
> CC: Jan Vrany <jan.vrany@labware.com>,
> 	Eli Zaretskii <eliz@gnu.org>
> Date: Thu, 21 Nov 2024 12:47:06 +0000
> 
> This commit adds code to allow user extension to instantiate
> gdb.Compunit. This is a step towards a Python support for dynamically
> generated code (JIT) in GDB.
> 
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> ---
>  gdb/doc/python.texi                      | 18 ++++-
>  gdb/python/py-compunit.c                 | 97 +++++++++++++++++++++++-
>  gdb/python/py-objfile.c                  | 12 +++
>  gdb/python/python-internal.h             |  1 +
>  gdb/testsuite/gdb.python/py-compunit.exp | 42 +++++++++-
>  5 files changed, 167 insertions(+), 3 deletions(-)

OK for the documentation part, thanks.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC v2 11/21] gdb/python: allow instantiation of gdb.Objfile from Python
  2024-11-21 12:47 ` [RFC v2 11/21] gdb/python: allow instantiation of gdb.Objfile from Python Jan Vrany
@ 2024-11-21 13:42   ` Eli Zaretskii
  0 siblings, 0 replies; 39+ messages in thread
From: Eli Zaretskii @ 2024-11-21 13:42 UTC (permalink / raw)
  To: Jan Vrany; +Cc: gdb-patches

> From: Jan Vrany <jan.vrany@labware.com>
> CC: Jan Vrany <jan.vrany@labware.com>,
> 	Eli Zaretskii <eliz@gnu.org>
> Date: Thu, 21 Nov 2024 12:47:04 +0000
> 
> This commit adds code to allow user extension to instantiate
> gdb.Objfile. This is a step towards a Python support for dynamically
> generated code (JIT) in GDB.
> 
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> ---
>  gdb/doc/python.texi                     |  13 +++
>  gdb/python/py-inferior.c                |  10 +++
>  gdb/python/py-objfile.c                 | 111 +++++++++++++++++++++++-
>  gdb/python/python-internal.h            |   1 +
>  gdb/testsuite/gdb.python/py-objfile.exp |  38 +++++++-
>  5 files changed, 169 insertions(+), 4 deletions(-)

The documentation part is okay, provided that this not is fixed:

> +The optional @var{inferior} argument specifies the inferior to which the newly
> +created objfile is added to.

The second "to" should be removed.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC v2 14/21] gdb/python: allow instantiation of gdb.Symtab from Python
  2024-11-21 12:47 ` [RFC v2 14/21] gdb/python: allow instantiation of gdb.Symtab " Jan Vrany
@ 2024-11-21 13:43   ` Eli Zaretskii
  0 siblings, 0 replies; 39+ messages in thread
From: Eli Zaretskii @ 2024-11-21 13:43 UTC (permalink / raw)
  To: Jan Vrany; +Cc: gdb-patches

> From: Jan Vrany <jan.vrany@labware.com>
> CC: Jan Vrany <jan.vrany@labware.com>,
> 	Eli Zaretskii <eliz@gnu.org>
> Date: Thu, 21 Nov 2024 12:47:07 +0000
> 
> This commit adds code to allow user extension to instantiate
> gdb.Symtab. This is a step towards a Python support for dynamically
> generated code (JIT) in GDB.
> 
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> ---
>  gdb/doc/python.texi                    |  4 ++
>  gdb/python/py-symtab.c                 | 53 +++++++++++++++++++++++++-
>  gdb/testsuite/gdb.python/py-symtab.exp | 15 ++++++++
>  3 files changed, 70 insertions(+), 2 deletions(-)

OK for the documentation part, thanks.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC v2 05/21] gdb/python: add function () method to gdb.Type object
  2024-11-21 13:34   ` Eli Zaretskii
@ 2024-11-21 14:06     ` Jan Vraný
  0 siblings, 0 replies; 39+ messages in thread
From: Jan Vraný @ 2024-11-21 14:06 UTC (permalink / raw)
  To: eliz; +Cc: gdb-patches

On Thu, 2024-11-21 at 15:34 +0200, Eli Zaretskii wrote:
> > From: Jan Vrany <jan.vrany@labware.com>
> > CC: Jan Vrany <jan.vrany@labware.com>,
> > 	Eli Zaretskii <eliz@gnu.org>
> > Date: Thu, 21 Nov 2024 12:46:58 +0000
> > 
> > This commit adds a new method to Python type objects that returns
> > possibly new function type returning that type. Parameter types can
> > be specified too.
> > 
> > This will be useful later to create types for function symbols
> > created
> > using Python extension code.
> > 
> > Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> > ---
> >  gdb/NEWS                             |  3 ++
> >  gdb/doc/python.texi                  |  8 +++++
> >  gdb/python/py-type.c                 | 54
> > ++++++++++++++++++++++++++++
> >  gdb/testsuite/gdb.python/py-type.exp | 20 +++++++++++
> >  4 files changed, 85 insertions(+)
> 
> The documentation parts are okay, but...
> 
> > +@defun Type.function (@r{[}param_type...@r{]})
> > +Return a new @code{gdb.Type} object which represents a type of
> > function
> > +returning this type.  @code{param_type...} arguments specify
> > parameter
> 
> ...please use @dots{} instead of literal "..." in both cases here,
> when you push.
> 

Thanks! I have fixed this in my local working version
and it will part of next version. Same for the extra "to"
in the other commit (
https://sourceware.org/pipermail/gdb-patches/2024-November/213479.html
)

Jan

> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> 


^ permalink raw reply	[flat|nested] 39+ messages in thread

end of thread, other threads:[~2024-11-21 14:06 UTC | newest]

Thread overview: 39+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-11-21 12:46 [RFC v2 00/21] Add Python "JIT" API Jan Vrany
2024-11-21 12:46 ` [RFC v2 01/21] gdb: update is_addr_in_objfile to support "dynamic" objfiles Jan Vrany
2024-11-21 12:46 ` [RFC v2 02/21] gdb/python: add subblocks property to gdb.Block Jan Vrany
2024-11-21 13:35   ` Eli Zaretskii
2024-11-21 12:46 ` [RFC v2 03/21] gdb/python: add domain property to gdb.Symbol Jan Vrany
2024-11-21 13:32   ` Eli Zaretskii
2024-11-21 12:46 ` [RFC v2 04/21] gdb/python: add void_type () method to gdb.Architecture object Jan Vrany
2024-11-21 13:32   ` Eli Zaretskii
2024-11-21 12:46 ` [RFC v2 05/21] gdb/python: add function () method to gdb.Type object Jan Vrany
2024-11-21 13:34   ` Eli Zaretskii
2024-11-21 14:06     ` Jan Vraný
2024-11-21 12:46 ` [RFC v2 06/21] gdb/python: add template function to implement equality comparison Jan Vrany
2024-11-21 12:47 ` [RFC v2 07/21] gdb/python: make gdb.Symbol comparable for equality Jan Vrany
2024-11-21 12:47 ` [RFC v2 08/21] gdb/python: make gdb.Symtab " Jan Vrany
2024-11-21 12:47 ` [RFC v2 09/21] gdb: use std::vector<> to hold on blocks in struct blockvector Jan Vrany
2024-11-21 12:47 ` [RFC v2 10/21] gdb/python: add gdb.Compunit Jan Vrany
2024-11-21 13:39   ` Eli Zaretskii
2024-11-21 12:47 ` [RFC v2 11/21] gdb/python: allow instantiation of gdb.Objfile from Python Jan Vrany
2024-11-21 13:42   ` Eli Zaretskii
2024-11-21 12:47 ` [RFC v2 12/21] gdb/python: add unlink () method to gdb.Objfile object Jan Vrany
2024-11-21 13:39   ` Eli Zaretskii
2024-11-21 12:47 ` [RFC v2 13/21] gdb/python: allow instantiation of gdb.Compunit from Python Jan Vrany
2024-11-21 13:40   ` Eli Zaretskii
2024-11-21 12:47 ` [RFC v2 14/21] gdb/python: allow instantiation of gdb.Symtab " Jan Vrany
2024-11-21 13:43   ` Eli Zaretskii
2024-11-21 12:47 ` [RFC v2 15/21] gdb/python: allow instantiation of gdb.Block " Jan Vrany
2024-11-21 13:35   ` Eli Zaretskii
2024-11-21 12:47 ` [RFC v2 16/21] gdb/python: allow instantiation of gdb.Symbol " Jan Vrany
2024-11-21 13:37   ` Eli Zaretskii
2024-11-21 12:47 ` [RFC v2 17/21] gdb/python: add add_symbol () method to gdb.Block Jan Vrany
2024-11-21 13:36   ` Eli Zaretskii
2024-11-21 12:47 ` [RFC v2 18/21] gdb/python: add more attributes to gdb.LinetableEntry objects Jan Vrany
2024-11-21 13:28   ` Eli Zaretskii
2024-11-21 12:47 ` [RFC v2 19/21] gdb/python: allow instantiation of gdb.LineTableEntry objects Jan Vrany
2024-11-21 13:27   ` Eli Zaretskii
2024-11-21 12:47 ` [RFC v2 20/21] gdb/python: allow instantiation of gdb.LineTable objects Jan Vrany
2024-11-21 13:29   ` Eli Zaretskii
2024-11-21 12:47 ` [RFC v2 21/21] gdb/python: add section in documentation on implementing JIT interface Jan Vrany
2024-11-21 13:31   ` Eli Zaretskii

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).