* [RFC v5 00/19] Add Python "JIT" API
@ 2025-06-23 16:09 Jan Vrany
2025-06-23 16:09 ` [RFC v5 01/18] gdb: introduce expand_symtabs_maybe_overlapping Jan Vrany
` (17 more replies)
0 siblings, 18 replies; 31+ messages in thread
From: Jan Vrany @ 2025-06-23 16:09 UTC (permalink / raw)
To: gdb-patches; +Cc: Jan Vrany
Hello,
I'm very sorry for the delay, I've been swamped with other work for last
couple months.
This is a v5 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/
The v2 submission is here:
https://inbox.sourceware.org/gdb-patches/20241121124714.419946-1-jan.vrany@labware.com/
The v3 submission is here:
https://inbox.sourceware.org/gdb-patches/20250129124347.1391900-1-jan.vrany@labware.com/
The v4 submission is here:
https://inbox.sourceware.org/gdb-patches/20250205161018.2028175-1-jan.vrany@labware.com/
Changes in v5:
* Move ranges_overlap utility to its own file (gdbsupport/range.h)
* Delegate compunit_symtab::maybe_contains to blockvector::maybe_contains.
* Reworked commit
gdb/python: add function () method to gdb.Type object
so that non-arch owned types are first copied to arch and then used.
This way, we do not need to expose internal implementation detail to the
user as Tom pointed out. This also means that commit
gdb/python: add owner property to gdb.Type
is no longer needed and has been dropped from this version.
See https://inbox.sourceware.org/gdb-patches/20250227222931.1404787-1-jan.vrany@labware.com/
* Dropped commits
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
They are no longer needed because recent change that made these objects
memoized.
See https://inbox.sourceware.org/gdb-patches/20250319212212.490510-7-jan.vrany@labware.com/
https://inbox.sourceware.org/gdb-patches/20250319212212.490510-9-jan.vrany@labware.com/
Thanks,
Jan
---
Jan Vrany (18):
gdb: introduce expand_symtabs_maybe_overlapping
gdb: introduce compunit_symtab::maybe_contains
gdb: update is_addr_in_objfile to support "dynamic" objfiles
gdb: introduce new function create_function_type
gdb/python: add function () method to gdb.Type object
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 | 9 +
gdb/block.c | 82 +++++
gdb/block.h | 69 +++-
gdb/buildsym.c | 6 +-
gdb/doc/gdb.texinfo | 3 +-
gdb/doc/python.texi | 317 +++++++++++++++++
gdb/dwarf2/cooked-index.h | 11 +
gdb/dwarf2/read.c | 11 +
gdb/dwarf2/read.h | 3 +
gdb/gdbtypes.c | 67 +++-
gdb/gdbtypes.h | 30 +-
gdb/jit.c | 8 +-
gdb/mdebugread.c | 32 +-
gdb/objfiles.c | 34 ++
gdb/objfiles.h | 14 +
gdb/psymtab.c | 21 ++
gdb/psymtab.h | 3 +
gdb/python/py-block.c | 165 ++++++++-
gdb/python/py-compunit.c | 411 ++++++++++++++++++++++
gdb/python/py-inferior.c | 10 +
gdb/python/py-linetable.c | 243 ++++++++++++-
gdb/python/py-objfile.c | 172 ++++++++-
gdb/python/py-symbol.c | 139 +++++++-
gdb/python/py-symtab.c | 67 +++-
gdb/python/py-type.c | 132 +++++++
gdb/python/python-internal.h | 5 +
gdb/quick-symbol.h | 5 +
gdb/symfile-debug.c | 16 +
gdb/symtab.c | 90 ++++-
gdb/symtab.h | 10 +
gdb/testsuite/gdb.base/jit-reader.exp | 9 +
gdb/testsuite/gdb.python/py-block.exp | 45 +++
gdb/testsuite/gdb.python/py-compunit-1.c | 24 ++
gdb/testsuite/gdb.python/py-compunit-2.c | 28 ++
gdb/testsuite/gdb.python/py-compunit.exp | 130 +++++++
gdb/testsuite/gdb.python/py-compunit.link | 10 +
gdb/testsuite/gdb.python/py-jit.c | 61 ++++
gdb/testsuite/gdb.python/py-jit.exp | 57 +++
gdb/testsuite/gdb.python/py-jit.py | 118 +++++++
gdb/testsuite/gdb.python/py-linetable.exp | 67 +++-
gdb/testsuite/gdb.python/py-objfile.exp | 50 ++-
gdb/testsuite/gdb.python/py-symbol.exp | 13 +
gdb/testsuite/gdb.python/py-symtab.exp | 22 ++
gdb/testsuite/gdb.python/py-type.exp | 36 ++
gdbsupport/gdb_obstack.h | 52 +++
gdbsupport/range.h | 35 ++
47 files changed, 2822 insertions(+), 121 deletions(-)
create mode 100644 gdb/python/py-compunit.c
create mode 100644 gdb/testsuite/gdb.python/py-compunit-1.c
create mode 100644 gdb/testsuite/gdb.python/py-compunit-2.c
create mode 100644 gdb/testsuite/gdb.python/py-compunit.exp
create mode 100644 gdb/testsuite/gdb.python/py-compunit.link
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
create mode 100644 gdbsupport/range.h
--
2.47.2
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC v5 01/18] gdb: introduce expand_symtabs_maybe_overlapping
2025-06-23 16:09 [RFC v5 00/19] Add Python "JIT" API Jan Vrany
@ 2025-06-23 16:09 ` Jan Vrany
2025-06-24 15:22 ` Tom Tromey
2025-06-23 16:09 ` [RFC v5 02/18] gdb: introduce compunit_symtab::maybe_contains Jan Vrany
` (16 subsequent siblings)
17 siblings, 1 reply; 31+ messages in thread
From: Jan Vrany @ 2025-06-23 16:09 UTC (permalink / raw)
To: gdb-patches; +Cc: Jan Vrany
This commit introduces expand_symtabs_maybe_overlapping, a new "quick
symbol function". This will be used later by Python API to create new
compunits to ensure to-be-created compunit does not overlap with
existing one.
For DWARF2 base index functions, all compunits are expanded
unconditionally since there seem to be no way to extract unexpanded
compunit's conservative address range.
---
gdb/dwarf2/cooked-index.h | 11 +++++++++++
gdb/dwarf2/read.c | 11 +++++++++++
gdb/dwarf2/read.h | 3 +++
gdb/objfiles.h | 3 +++
gdb/psymtab.c | 21 +++++++++++++++++++++
gdb/psymtab.h | 3 +++
gdb/quick-symbol.h | 5 +++++
gdb/symfile-debug.c | 16 ++++++++++++++++
gdbsupport/range.h | 35 +++++++++++++++++++++++++++++++++++
9 files changed, 108 insertions(+)
create mode 100644 gdbsupport/range.h
diff --git a/gdb/dwarf2/cooked-index.h b/gdb/dwarf2/cooked-index.h
index 43b27232aec..894f2078e9f 100644
--- a/gdb/dwarf2/cooked-index.h
+++ b/gdb/dwarf2/cooked-index.h
@@ -237,6 +237,17 @@ struct cooked_index_functions : public dwarf2_base_index_functions
dwarf2_base_index_functions::expand_all_symtabs (objfile);
}
+ /* Cooked index's version of expand_symtabs_maybe_overlapping. See its
+ definition in the definition of quick_symbol_functions in symfile.h. */
+
+ void expand_symtabs_maybe_overlapping (struct objfile *objfile,
+ CORE_ADDR start, CORE_ADDR end) override
+ {
+ wait (objfile, true);
+ dwarf2_base_index_functions::expand_symtabs_maybe_overlapping
+ (objfile, start, end);
+ }
+
bool expand_symtabs_matching
(struct objfile *objfile,
expand_symtabs_file_matcher file_matcher,
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 2f27b7cff4c..3270ce55c56 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -1926,6 +1926,17 @@ dwarf2_base_index_functions::expand_all_symtabs (struct objfile *objfile)
}
}
+/* DWARF2 index's version of expand_symtabs_maybe_overlapping. See its definition
+ in the definition of quick_symbol_functions in symfile.h. */
+
+void
+dwarf2_base_index_functions::expand_symtabs_maybe_overlapping
+ (struct objfile *objfile, CORE_ADDR start, CORE_ADDR end)
+{
+ /* Simply expand all symtabs. I do not know how to extract (if at all
+ possible) conservative address range from dwarf2_per_cu_data. */
+ expand_all_symtabs (objfile);
+}
/* See read.h. */
bool
diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
index a5cfb3144f7..da55f993123 100644
--- a/gdb/dwarf2/read.h
+++ b/gdb/dwarf2/read.h
@@ -1192,6 +1192,9 @@ struct dwarf2_base_index_functions : public quick_symbol_functions
void expand_all_symtabs (struct objfile *objfile) override;
+ virtual void expand_symtabs_maybe_overlapping (struct objfile *objfile,
+ CORE_ADDR start, CORE_ADDR end) override;
+
struct compunit_symtab *find_pc_sect_compunit_symtab
(struct objfile *objfile, bound_minimal_symbol msymbol,
CORE_ADDR pc, struct obj_section *section, int warn_if_readin)
diff --git a/gdb/objfiles.h b/gdb/objfiles.h
index 4a3475862a9..0206b49e00c 100644
--- a/gdb/objfiles.h
+++ b/gdb/objfiles.h
@@ -597,6 +597,9 @@ struct objfile : intrusive_list_node<objfile>
code, e.g., DW_TAG_type_unit for dwarf debug info. */
void expand_symtabs_with_fullname (const char *fullname);
+ /* See quick_symbol_functions. */
+ void expand_symtabs_maybe_overlapping (CORE_ADDR start, CORE_ADDR end);
+
/* See quick_symbol_functions. */
bool expand_symtabs_matching
(expand_symtabs_file_matcher file_matcher,
diff --git a/gdb/psymtab.c b/gdb/psymtab.c
index 986ef44f6c7..d40afd5877f 100644
--- a/gdb/psymtab.c
+++ b/gdb/psymtab.c
@@ -36,6 +36,7 @@
#include <algorithm>
#include <set>
#include "gdbsupport/buildargv.h"
+#include "gdbsupport/range.h"
static const struct partial_symbol *lookup_partial_symbol
(struct objfile *, struct partial_symtab *, const lookup_name_info &,
@@ -731,6 +732,26 @@ psymbol_functions::expand_all_symtabs (struct objfile *objfile)
psymtab_to_symtab (objfile, psymtab);
}
+/* Psymtab version of expand_symtabs_maybe_overlapping. See its definition in
+ the definition of quick_symbol_functions in symfile.h. */
+
+void
+psymbol_functions::expand_symtabs_maybe_overlapping
+ (struct objfile *objfile, CORE_ADDR start, CORE_ADDR end)
+{
+ for (partial_symtab *psymtab : partial_symbols (objfile))
+ {
+ if (psymtab->text_low_valid && psymtab->text_high_valid)
+ {
+ CORE_ADDR text_low = psymtab->text_low (objfile);
+ CORE_ADDR text_high = psymtab->text_high (objfile);
+
+ if (ranges_overlap (start, end, text_low, text_high))
+ psymtab_to_symtab (objfile, psymtab);
+ }
+ }
+}
+
/* Psymtab version of map_symbol_filenames. See its definition in
the definition of quick_symbol_functions in symfile.h. */
diff --git a/gdb/psymtab.h b/gdb/psymtab.h
index ad0b2ff4234..8a8ea738bbd 100644
--- a/gdb/psymtab.h
+++ b/gdb/psymtab.h
@@ -626,6 +626,9 @@ struct psymbol_functions : public quick_symbol_functions
void expand_all_symtabs (struct objfile *objfile) override;
+ virtual void expand_symtabs_maybe_overlapping (struct objfile *objfile,
+ CORE_ADDR start, CORE_ADDR end) override;
+
bool expand_symtabs_matching
(struct objfile *objfile,
expand_symtabs_file_matcher file_matcher,
diff --git a/gdb/quick-symbol.h b/gdb/quick-symbol.h
index 9db1f1860cb..899f2f22239 100644
--- a/gdb/quick-symbol.h
+++ b/gdb/quick-symbol.h
@@ -130,6 +130,11 @@ struct quick_symbol_functions
/* Read all symbol tables associated with OBJFILE. */
virtual void expand_all_symtabs (struct objfile *objfile) = 0;
+ /* Read all symbol tables associated with OBJFILE which may overlap
+ with range [START, END). */
+ virtual void expand_symtabs_maybe_overlapping (struct objfile *objfile,
+ CORE_ADDR start, CORE_ADDR end) = 0;
+
/* Expand all symbol tables in OBJFILE matching some criteria.
If LANG_MATCHER returns false, expansion of the symbol table may be
diff --git a/gdb/symfile-debug.c b/gdb/symfile-debug.c
index 9c5ce85d26e..900d1bf90e2 100644
--- a/gdb/symfile-debug.c
+++ b/gdb/symfile-debug.c
@@ -343,6 +343,22 @@ objfile::expand_all_symtabs ()
iter->expand_all_symtabs (this);
}
+/* See objfiles.h. */
+
+void
+objfile::expand_symtabs_maybe_overlapping (CORE_ADDR start, CORE_ADDR end)
+{
+ if (debug_symfile)
+ gdb_printf (gdb_stdlog,
+ "qf->expand_symtabs_maybe_overlapping (%s, %s, %s)\n",
+ objfile_debug_name (this),
+ hex_string (start),
+ hex_string (end));
+
+ for (const auto &iter : qf)
+ iter->expand_symtabs_maybe_overlapping (this, start, end);
+}
+
void
objfile::expand_symtabs_with_fullname (const char *fullname)
{
diff --git a/gdbsupport/range.h b/gdbsupport/range.h
new file mode 100644
index 00000000000..b665943cfd3
--- /dev/null
+++ b/gdbsupport/range.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2025-2025 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/>. */
+
+#ifndef GDBSUPPORT_RANGE_H
+#define GDBSUPPORT_RANGE_H
+
+/* Return true if given ranges [AL, AH) and [BL, BH) overlap. Return false
+ otherwise. */
+
+template <typename T>
+bool ranges_overlap (T al, T ah, T bl, T bh)
+{
+ static_assert (std::is_integral<T>::value, "Integral type required");
+
+ gdb_assert (al <= ah);
+ gdb_assert (bl <= bh);
+
+ return !(ah <= bl || bh <= al);
+}
+
+#endif /* GDBSUPPORT_RANGE_H */
--
2.47.2
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC v5 02/18] gdb: introduce compunit_symtab::maybe_contains
2025-06-23 16:09 [RFC v5 00/19] Add Python "JIT" API Jan Vrany
2025-06-23 16:09 ` [RFC v5 01/18] gdb: introduce expand_symtabs_maybe_overlapping Jan Vrany
@ 2025-06-23 16:09 ` Jan Vrany
2025-06-23 16:09 ` [RFC v5 03/18] gdb: update is_addr_in_objfile to support "dynamic" objfiles Jan Vrany
` (15 subsequent siblings)
17 siblings, 0 replies; 31+ messages in thread
From: Jan Vrany @ 2025-06-23 16:09 UTC (permalink / raw)
To: gdb-patches; +Cc: Jan Vrany, Tom Tromey
This commit introduces new method - compunit_symtab::maybe_contains -
returning true if a compunit_symtab may contain given address or false
if it definitely cannot. This means, compunit_symtab::maybe_contains
may return true even if in fact the compunit does not contain the
address.
This may happen for native ecoff format, as described in a comment in
find_pc_sect_compunit_symtab (symtab.c).
The second case described in that command (when objfiles that have their
functions reordered) should be handled, in that DWARF reader sets
blockvector's map accordingly and maybe_contains consults that map.
Approved-By: Tom Tromey <tom@tromey.com>
---
gdb/block.c | 23 +++++++++++++++++++++++
gdb/block.h | 4 ++++
gdb/symtab.c | 26 ++++++++++++--------------
gdb/symtab.h | 4 ++++
4 files changed, 43 insertions(+), 14 deletions(-)
diff --git a/gdb/block.c b/gdb/block.c
index fdf209c86a2..54d768d7255 100644
--- a/gdb/block.c
+++ b/gdb/block.c
@@ -821,6 +821,29 @@ make_blockranges (struct objfile *objfile,
return blr;
}
+/* See block.h. */
+
+bool blockvector::maybe_contains (CORE_ADDR addr) const
+{
+ const CORE_ADDR start = global_block ()->start ();
+ const CORE_ADDR end = global_block ()->end ();
+
+ /* Check if given address falls into global block. If not, this compunit
+ definitely does not contain ADDR. */
+ if (addr < start || end <= addr)
+ return false;
+
+ /* A compunit may span over multiple disjoint regions (see comment in
+ find_pc_sect_compunit_symtab). DWARF reader sets address map mapping
+ addresses to blocks. So if the map is set and there is no block
+ mapped at ADDR, this compunit definitely does not contain ADDR. */
+ if (map () != nullptr && map ()->find (addr) == nullptr)
+ return false;
+
+ /* Otherwise, this compunit may or may not contain ADDR. */
+ return true;
+}
+
/* 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 76fa203697c..4a962eff30e 100644
--- a/gdb/block.h
+++ b/gdb/block.h
@@ -483,6 +483,10 @@ struct blockvector
void set_map (addrmap_fixed *map)
{ m_map = map; }
+ /* True if this blockvector contains block at ADDR, false if
+ it definitely does not. */
+ bool maybe_contains (CORE_ADDR addr) const;
+
private:
/* An address map mapping addresses to blocks in this blockvector.
This pointer is zero if the blocks' start and end addresses are
diff --git a/gdb/symtab.c b/gdb/symtab.c
index b2de990143d..506e21eeac4 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -529,6 +529,14 @@ compunit_symtab::forget_cached_source_info ()
/* See symtab.h. */
+bool
+compunit_symtab::maybe_contains (CORE_ADDR addr) const
+{
+ return this->blockvector ()->maybe_contains (addr);
+}
+
+/* See symtab.h. */
+
void
compunit_symtab::finalize ()
{
@@ -2931,23 +2939,13 @@ find_pc_sect_compunit_symtab (CORE_ADDR pc, struct obj_section *section)
{
for (compunit_symtab *cust : obj_file->compunits ())
{
- const struct blockvector *bv = cust->blockvector ();
- const struct block *global_block = bv->global_block ();
- CORE_ADDR start = global_block->start ();
- CORE_ADDR end = global_block->end ();
- bool in_range_p = start <= pc && pc < end;
- if (!in_range_p)
+ if (!cust->maybe_contains (pc))
continue;
- if (bv->map () != nullptr)
- {
- if (bv->map ()->find (pc) == nullptr)
- continue;
-
- return cust;
- }
+ const struct blockvector *bv = cust->blockvector ();
+ CORE_ADDR range =
+ bv->global_block ()->end () - bv->global_block ()->start ();
- CORE_ADDR range = end - start;
if (best_cust != nullptr
&& range >= best_cust_range)
/* Cust doesn't have a smaller range than best_cust, skip it. */
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 0a57be5ed80..7f084cef691 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1963,6 +1963,10 @@ struct compunit_symtab
compunit_symtab objects are allocated on an obstack. */
void finalize ();
+ /* True if ADDR may be in this compunit_symtab, false if
+ definitely is not. */
+ bool maybe_contains (CORE_ADDR addr) const;
+
/* Unordered chain of all compunit symtabs of this objfile. */
struct compunit_symtab *next;
--
2.47.2
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC v5 03/18] gdb: update is_addr_in_objfile to support "dynamic" objfiles
2025-06-23 16:09 [RFC v5 00/19] Add Python "JIT" API Jan Vrany
2025-06-23 16:09 ` [RFC v5 01/18] gdb: introduce expand_symtabs_maybe_overlapping Jan Vrany
2025-06-23 16:09 ` [RFC v5 02/18] gdb: introduce compunit_symtab::maybe_contains Jan Vrany
@ 2025-06-23 16:09 ` Jan Vrany
2025-06-23 16:09 ` [RFC v5 04/18] gdb: introduce new function create_function_type Jan Vrany
` (14 subsequent siblings)
17 siblings, 0 replies; 31+ messages in thread
From: Jan Vrany @ 2025-06-23 16:09 UTC (permalink / raw)
To: gdb-patches; +Cc: Jan Vrany, Tom Tromey
While working with objfiles in Python I noticed that
gdb.Progspace.objfile_for_address () does not return "dynamic" objfiles
created by (for example) GDB's JIT reader API.
This is because is_addr_in_objfile() checks if a 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 the address fall into any compunit in that objfile. It does so
only if the objfile has no sections.
Approved-By: Tom Tromey <tom@tromey.com>
---
gdb/objfiles.c | 14 ++++++++++++++
gdb/objfiles.h | 7 +++++++
gdb/testsuite/gdb.base/jit-reader.exp | 9 +++++++++
3 files changed, 30 insertions(+)
diff --git a/gdb/objfiles.c b/gdb/objfiles.c
index d25d1a02cea..6d5dd588f91 100644
--- a/gdb/objfiles.c
+++ b/gdb/objfiles.c
@@ -1135,6 +1135,20 @@ 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 any of them contains given ADDR. */
+ if (objfile->sections_start == nullptr)
+ {
+ for (const compunit_symtab *cu : objfile->compunits ())
+ {
+ if (cu->maybe_contains (addr))
+ return true;
+ }
+ }
return false;
}
diff --git a/gdb/objfiles.h b/gdb/objfiles.h
index 0206b49e00c..cadb4525782 100644
--- a/gdb/objfiles.h
+++ b/gdb/objfiles.h
@@ -459,6 +459,13 @@ struct objfile : intrusive_list_node<objfile>
return compunit_symtab_range (compunit_symtabs);
}
+ /* Const version of the function above. */
+
+ compunit_symtab_range compunits () const
+ {
+ return compunit_symtab_range (compunit_symtabs);
+ }
+
/* A range adapter that makes it possible to iterate over all
minimal symbols of an objfile. */
diff --git a/gdb/testsuite/gdb.base/jit-reader.exp b/gdb/testsuite/gdb.base/jit-reader.exp
index 4462ab4aa65..8399558403a 100644
--- a/gdb/testsuite/gdb.base/jit-reader.exp
+++ b/gdb/testsuite/gdb.base/jit-reader.exp
@@ -233,6 +233,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.47.2
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC v5 04/18] gdb: introduce new function create_function_type
2025-06-23 16:09 [RFC v5 00/19] Add Python "JIT" API Jan Vrany
` (2 preceding siblings ...)
2025-06-23 16:09 ` [RFC v5 03/18] gdb: update is_addr_in_objfile to support "dynamic" objfiles Jan Vrany
@ 2025-06-23 16:09 ` Jan Vrany
2025-06-24 15:29 ` Tom Tromey
2025-06-23 16:10 ` [RFC v5 05/18] gdb/python: add function () method to gdb.Type object Jan Vrany
` (13 subsequent siblings)
17 siblings, 1 reply; 31+ messages in thread
From: Jan Vrany @ 2025-06-23 16:09 UTC (permalink / raw)
To: gdb-patches; +Cc: Jan Vrany
While working on new Python API to create new function types I realized
that there's no easy way to create a new function type and control where
it is going to be allocated (whether in gdbarch's obstack pr objfile's).
Functions lookup_function_type and lookup_function_type_with_arguments
always allocate at the same obstack as its return type.
This is not sufficient for the new Python API - the user may use any
type it can get hold of. For example, one may want to create a function
returning arch-owned type and taking one argument of objfile-owned type.
In that case we need to allocate the new type on that very objfile's
obstack.
This commit introduces new function - create_function_type - that takes
type_allocator as first parameter, allowing caller to control the
allocation. Existing functions (lookup_function_type and
lookup_function_type_with_arguments) are reimplemented by means of new
create_function_type.
---
gdb/gdbtypes.c | 47 +++++++++++++++++++++++++++++------------------
gdb/gdbtypes.h | 24 +++++++++++++++++++-----
2 files changed, 48 insertions(+), 23 deletions(-)
diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c
index 9c57f30a1dd..591a3cc4323 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -561,27 +561,19 @@ make_function_type (struct type *type, struct type **typeptr)
return ntype;
}
-/* Given a type TYPE, return a type of functions that return that type.
- May need to construct such a type if this is the first use. */
-
-struct type *
-lookup_function_type (struct type *type)
-{
- return make_function_type (type, (struct type **) 0);
-}
-
-/* Given a type TYPE and argument types, return the appropriate
- function type. If the final type in PARAM_TYPES is NULL, make a
- varargs function. */
+/* See gdbtypes.h. */
struct type *
-lookup_function_type_with_arguments (struct type *type,
- int nparams,
- struct type **param_types)
+create_function_type (type_allocator &alloc,
+ struct type *return_type,
+ int nparams,
+ struct type **param_types)
{
- struct type *fn = make_function_type (type, (struct type **) 0);
+ struct type *fn = alloc.new_type ();
int i;
+ make_function_type (return_type, &fn);
+
if (nparams > 0)
{
if (param_types[nparams - 1] == NULL)
@@ -589,8 +581,7 @@ lookup_function_type_with_arguments (struct type *type,
--nparams;
fn->set_has_varargs (true);
}
- else if (check_typedef (param_types[nparams - 1])->code ()
- == TYPE_CODE_VOID)
+ else if ((param_types[nparams - 1])->is_void ())
{
--nparams;
/* Caller should have ensured this. */
@@ -608,6 +599,26 @@ lookup_function_type_with_arguments (struct type *type,
return fn;
}
+/* See gdbtypes.h. */
+
+struct type *
+lookup_function_type (struct type *return_type)
+{
+ type_allocator alloc (return_type);
+ return create_function_type (alloc, return_type, 0, nullptr);
+}
+
+/* See gdbtypes.h. */
+
+struct type *
+lookup_function_type_with_arguments (struct type *return_type,
+ int nparams,
+ struct type **param_types)
+{
+ type_allocator alloc (return_type);
+ return create_function_type (alloc, return_type, nparams, param_types);
+}
+
/* Identify address space identifier by name -- return a
type_instance_flags. */
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index 9e2efe99cff..ba216c6db54 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -2526,11 +2526,25 @@ extern struct type *lookup_pointer_type (struct type *);
extern struct type *make_function_type (struct type *, struct type **);
-extern struct type *lookup_function_type (struct type *);
-
-extern struct type *lookup_function_type_with_arguments (struct type *,
- int,
- struct type **);
+/* Given a return type and argument types, create new function type.
+ If the final type in PARAM_TYPES is NULL, create a varargs function.
+ New type is allocated using ALLOC. */
+extern struct type *create_function_type (type_allocator &alloc,
+ struct type *return_type,
+ int nparams,
+ struct type **param_types);
+
+/* Like create_function_type, but allocate the new function type at
+ the same obstack as RETURN_TYPE and with unspecified number of
+ parameters and their types. */
+extern struct type *lookup_function_type (struct type *return_type);
+
+/* Like create_function_type, but allocate the new function type at
+ the same obstack as RETURN_TYPE. */
+extern struct type *lookup_function_type_with_arguments
+ (struct type *return_type,
+ int nparams,
+ struct type **param_types);
/* Create a range type using ALLOC.
--
2.47.2
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC v5 05/18] gdb/python: add function () method to gdb.Type object
2025-06-23 16:09 [RFC v5 00/19] Add Python "JIT" API Jan Vrany
` (3 preceding siblings ...)
2025-06-23 16:09 ` [RFC v5 04/18] gdb: introduce new function create_function_type Jan Vrany
@ 2025-06-23 16:10 ` Jan Vrany
2025-06-24 16:11 ` Tom Tromey
2025-06-23 16:10 ` [RFC v5 06/18] gdb: use std::vector<> to hold on blocks in struct blockvector Jan Vrany
` (12 subsequent siblings)
17 siblings, 1 reply; 31+ messages in thread
From: Jan Vrany @ 2025-06-23 16:10 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.
Unlike previous version, this commit copies all objfile-owned types
passed to gdb.Type.function to make them arch-owned. This way we do not
have to expose implementation details of type ownership to user.
This commit also prompted creation of type::printable_name in order to
produce nicer error message when user wants use types from different
architectures to create a new function type.
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
gdb/NEWS | 3 +
gdb/doc/python.texi | 11 +++
gdb/gdbtypes.c | 20 ++++
gdb/gdbtypes.h | 6 ++
gdb/python/py-type.c | 132 +++++++++++++++++++++++++++
gdb/testsuite/gdb.python/py-type.exp | 36 ++++++++
6 files changed, 208 insertions(+)
diff --git a/gdb/NEWS b/gdb/NEWS
index 6c8a008d39d..e8e32a5dd5c 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -330,6 +330,9 @@ vFile:stat
** 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 6fa22851f6a..633526dc875 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -1594,6 +1594,17 @@ Return a new @code{gdb.Type} object which represents a pointer to this
type.
@end defun
+@defun Type.function (@r{[}param_type@dots{}@r{]}, @r{[}varargs@r{]})
+Return a new @code{gdb.Type} object which represents a type of function
+returning this type. Returned function type is always marked as prototyped.
+
+@var{param_type@dots{}} positional-only arguments specify parameter types.
+Passing @code{void} type is not allowed.
+
+The optional @var{varargs} keyword-only argument specifies whether to create
+vararg function type. Defaults to @code{False}.
+@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/gdbtypes.c b/gdb/gdbtypes.c
index 591a3cc4323..2dc5b7ff417 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -45,6 +45,7 @@
#include "rust-lang.h"
#include "ada-lang.h"
#include "extract-store-integer.h"
+#include "typeprint.h"
/* The value of an invalid conversion badness. */
#define INVALID_CONVERSION 100
@@ -5975,6 +5976,25 @@ type::is_array_like ()
return defn->is_array_like (this);
}
+/* See gdbtypes.h. */
+
+bool
+type::is_void ()
+{
+ return check_typedef (this)->code () == TYPE_CODE_VOID;
+}
+
+const std::string
+type::printable_name () const
+{
+ string_file thetype;
+
+ current_language->print_type (const_cast<type*>(this), "",
+ &thetype, -1, 0,
+ &type_print_raw_options);
+
+ return thetype.release ();
+}
\f
static const registry<gdbarch>::key<struct builtin_type> gdbtypes_data;
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index ba216c6db54..b736d366fda 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -1054,6 +1054,9 @@ struct type
this->main_type->name = name;
}
+ /* Get the name of this type suitable for the user. */
+ const std::string printable_name () const;
+
/* Note that if thistype is a TYPEDEF type, you have to call check_typedef.
But check_typedef does set the TYPE_LENGTH of the TYPEDEF type,
so you only have to call check_typedef once. Since value::allocate
@@ -1526,6 +1529,9 @@ struct type
representations of arrays by the type's language. */
bool is_array_like ();
+ /* Return true if this type is "void". Follows typedefs. */
+ bool is_void ();
+
/* Return the language that this type came from. */
enum language language () const
{ return main_type->m_lang; }
diff --git a/gdb/python/py-type.c b/gdb/python/py-type.c
index c546aa7536a..3d6db708f8d 100644
--- a/gdb/python/py-type.c
+++ b/gdb/python/py-type.c
@@ -768,6 +768,135 @@ typy_unqualified (PyObject *self, PyObject *args)
return type_to_type_object (type);
}
+/* Return a function type. */
+
+static PyObject *
+typy_function (PyObject *self, PyObject *args, PyObject *kw)
+{
+ struct type *return_type = ((type_object *) self)->type;
+
+ /* Process arguments. We cannot simply use PyArg_ParseTupleAndKeywords
+ because this method take variable number of arguments. */
+ if (args == nullptr || !PyTuple_Check (args))
+ {
+ PyErr_Format (PyExc_ValueError,
+ _("Arguments is %s, not a tuple."),
+ Py_TYPE (args)->tp_name);
+ return nullptr;
+ }
+ int nparam_types = PyTuple_Size (args);
+ if (nparam_types == -1)
+ {
+ /* Can this really happen? At this point we _know_ that
+ ARGS is a tuple (or subclass). */
+ PyErr_Format (PyExc_ValueError,
+ _("Failed retrieve number of parameters."));
+ return nullptr;
+ }
+ std::vector<struct type *> param_types (nparam_types);
+ for (int i = 0; i < nparam_types; i++)
+ {
+ PyObject *param_type_obj = PySequence_GetItem (args, i);
+
+ if (param_type_obj == nullptr)
+ {
+ PyErr_Format (PyExc_ValueError,
+ _("Failed to retrieve parameter at index %d."), i);
+ return nullptr;
+ }
+ else
+ {
+ param_types[i] = type_object_to_type (param_type_obj);
+ if (param_types[i] == nullptr)
+ {
+ PyErr_Format (PyExc_TypeError,
+ _("Argument at index %d is %s, not a gdb.Type "
+ "object."),
+ i, Py_TYPE (param_type_obj)->tp_name);
+ return nullptr;
+ }
+ if (param_types[i]->is_void ())
+ {
+ PyErr_Format (PyExc_ValueError,
+ _("Argument at index %d is a void type but "
+ "void as parameter type is not allowed."),
+ i);
+ return nullptr;
+ }
+ }
+ }
+
+ if (kw != nullptr)
+ {
+ if (!PyDict_Check (kw))
+ {
+ PyErr_Format (PyExc_ValueError,
+ _("Arguments is %s, not a dict."),
+ Py_TYPE (args)->tp_name);
+ return nullptr;
+ }
+
+ PyObject *key, *value;
+ Py_ssize_t pos = 0;
+ while (PyDict_Next (kw, &pos, &key, &value))
+ {
+ if (!PyUnicode_Check (key))
+ {
+ PyErr_Format (PyExc_ValueError,
+ _("Keyword argument key is %s, not a string."),
+ Py_TYPE (key)->tp_name);
+ return nullptr;
+ }
+ if (PyUnicode_CompareWithASCIIString (key, "varargs") != 0)
+ {
+ PyErr_Format (PyExc_ValueError,
+ _("Invalid keyword argument \"%U\"."),
+ key);
+ return nullptr;
+ }
+ if (!PyBool_Check (value))
+ {
+ PyErr_Format (PyExc_ValueError,
+ _("Value of \"varargs\" argument is \"%s\", "
+ "not a bool."),
+ Py_TYPE (value)->tp_name);
+ return nullptr;
+ }
+ if (value == Py_True)
+ {
+ param_types.push_back (nullptr);
+ }
+ }
+ }
+
+ /* Copy all objfile-owned types to arch. This way user does not
+ need to care about mixing types from different objfiles and
+ architectures and we do not need to expose this implementation
+ detail to the user. */
+
+ copied_types_hash_t copied_types;
+ return_type = copy_type_recursive (return_type, copied_types);
+ for (int i = 0; i < param_types.size (); i++)
+ {
+ if (param_types[i] != nullptr)
+ param_types[i] = copy_type_recursive (param_types[i], copied_types);
+ }
+
+ struct type *function_type = nullptr;
+ try
+ {
+ function_type = lookup_function_type_with_arguments
+ (return_type, param_types.size (), param_types.data ());
+ function_type->set_is_prototyped (true);
+ }
+ catch (const gdb_exception &except)
+ {
+ return gdbpy_handle_gdb_exception (nullptr, except);
+ }
+
+ return type_to_type_object (function_type);
+}
+
/* Return the size of the type represented by SELF, in bytes. */
static PyObject *
typy_get_sizeof (PyObject *self, void *closure)
@@ -1630,6 +1759,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", (PyCFunction) typy_function, METH_VARARGS | METH_KEYWORDS,
+ "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 0bc4556c653..876ef474a32 100644
--- a/gdb/testsuite/gdb.python/py-type.exp
+++ b/gdb/testsuite/gdb.python/py-type.exp
@@ -378,6 +378,42 @@ 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_no_output "python uu_t = gdb.parse_and_eval ('uu').type"
+
+ gdb_test "python print(repr(int_t.function()))" \
+ "<gdb.Type code=TYPE_CODE_FUNC name=int \\(void\\)>"
+
+ 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, varargs=True)))" \
+ "<gdb.Type code=TYPE_CODE_FUNC name=int \\(int, ...\\)>"
+
+ gdb_test "python print(repr(int_t.function(varargs=True)))" \
+ "<gdb.Type code=TYPE_CODE_FUNC name=int \\(void\\)>"
+
+ gdb_test "python print(repr(int_t.function(varargs=False)))" \
+ "<gdb.Type code=TYPE_CODE_FUNC name=int \\(void\\)>"
+
+ gdb_test "python print(repr(int_t.function(123)))" \
+ "TypeError.*:.*"
+
+ gdb_test "python print(repr(int_t.function(gdb.lookup_type('void'))))" \
+ "ValueError.*:.*"
+
+ gdb_test "python print(repr(int_t.function(int_t, gdb.lookup_type('void'))))" \
+ "ValueError.*:.*"
+
+ gdb_test "python print(repr(int_t.function(int_t, varargs=\[1,2,3\])))" \
+ "ValueError.*:.*"
+
+ gdb_test "python print(repr(int_t.function(int_t, kw=False)))" \
+ "ValueError.*:.*"
+
+ gdb_test "python print(repr(int_t.function(gdb.lookup_type('void'), varargs=True)))" \
+ "ValueError.*:.*"
+
set sint [get_sizeof int 0]
gdb_test "python print(gdb.parse_and_eval('aligncheck').type.alignof)" \
$sint
--
2.47.2
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC v5 06/18] gdb: use std::vector<> to hold on blocks in struct blockvector
2025-06-23 16:09 [RFC v5 00/19] Add Python "JIT" API Jan Vrany
` (4 preceding siblings ...)
2025-06-23 16:10 ` [RFC v5 05/18] gdb/python: add function () method to gdb.Type object Jan Vrany
@ 2025-06-23 16:10 ` Jan Vrany
2025-06-23 16:10 ` [RFC v5 07/18] gdb/python: add gdb.Compunit Jan Vrany
` (11 subsequent siblings)
17 siblings, 0 replies; 31+ messages in thread
From: Jan Vrany @ 2025-06-23 16:10 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 54d768d7255..5e2c121cb3f 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"
@@ -844,6 +845,64 @@ bool blockvector::maybe_contains (CORE_ADDR addr) const
return true;
}
+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 4a962eff30e..89ceeb4268b 100644
--- a/gdb/block.h
+++ b/gdb/block.h
@@ -20,6 +20,7 @@
#ifndef GDB_BLOCK_H
#define GDB_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 ()
@@ -487,19 +507,23 @@ struct blockvector
it definitely does not. */
bool maybe_contains (CORE_ADDR addr) const;
+ 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 6dc079f29b1..a9ad8a997d3 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 1944e8afa22..a2512b9b9a9 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -518,7 +518,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;
@@ -553,18 +552,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 51e1907c683..25139e8ab1e 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 *);
@@ -4502,17 +4500,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.
@@ -4635,7 +4624,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 ());
@@ -4703,21 +4692,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 755b90767aa..2ac7288226f 100644
--- a/gdbsupport/gdb_obstack.h
+++ b/gdbsupport/gdb_obstack.h
@@ -20,6 +20,8 @@
#ifndef GDBSUPPORT_GDB_OBSTACK_H
#define GDBSUPPORT_GDB_OBSTACK_H
+#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 /* GDBSUPPORT_GDB_OBSTACK_H */
--
2.47.2
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC v5 07/18] gdb/python: add gdb.Compunit
2025-06-23 16:09 [RFC v5 00/19] Add Python "JIT" API Jan Vrany
` (5 preceding siblings ...)
2025-06-23 16:10 ` [RFC v5 06/18] gdb: use std::vector<> to hold on blocks in struct blockvector Jan Vrany
@ 2025-06-23 16:10 ` Jan Vrany
2025-06-23 16:10 ` [RFC v5 08/18] gdb/python: allow instantiation of gdb.Objfile from Python Jan Vrany
` (10 subsequent siblings)
17 siblings, 0 replies; 31+ messages in thread
From: Jan Vrany @ 2025-06-23 16:10 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 | 75 ++++++
gdb/python/py-block.c | 14 +
gdb/python/py-compunit.c | 311 ++++++++++++++++++++++
gdb/python/py-objfile.c | 31 +++
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-1.c | 24 ++
gdb/testsuite/gdb.python/py-compunit-2.c | 28 ++
gdb/testsuite/gdb.python/py-compunit.exp | 83 ++++++
gdb/testsuite/gdb.python/py-compunit.link | 10 +
gdb/testsuite/gdb.python/py-symtab.exp | 8 +
14 files changed, 606 insertions(+)
create mode 100644 gdb/python/py-compunit.c
create mode 100644 gdb/testsuite/gdb.python/py-compunit-1.c
create mode 100644 gdb/testsuite/gdb.python/py-compunit-2.c
create mode 100644 gdb/testsuite/gdb.python/py-compunit.exp
create mode 100644 gdb/testsuite/gdb.python/py-compunit.link
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 998203ce1e2..c74031891be 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -440,6 +440,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 e8e32a5dd5c..8e381329f9a 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -333,6 +333,8 @@ vFile:stat
** 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 633526dc875..3ef3699d355 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
@@ -5828,6 +5829,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 @code{gdb.Compunit} associated with this objfile.
+@xref{Compunits In Python}.
+@end defun
+
@node Frames In Python
@subsubsection Accessing inferior stack frames from Python
@@ -6180,6 +6186,11 @@ have a superblock that is not the static block -- for instance this
happens for an inlined function.
@end defvar
+@defvar Block.compunit
+The @code{gdb.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.
@@ -6613,6 +6624,11 @@ If no producer information is available then @code{None} is returned.
This attribute is not writable.
@end defvar
+@defvar Symtab.compunit
+The @code{gdb.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 ()
@@ -6642,6 +6658,65 @@ 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
+
+A compilation unit -- compunit for short -- represents a piece of code
+compiled by compiler independently of the rest. It is a result of compiling
+(parts of) one or more source files.
+
+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 @code{gdb.Objfile} from which the compunit was extracted.
+@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 sequence of @code{gdb.Symtab} objects 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. Similarly, accessing
+any of the attributes will throw an exception if it is invalid at the time
+the attribute is accessed.
+@end defun
+
+@defun Compunit.global_block ()
+Return @code{gdb.Block} object representing the global block of the
+underlying compunit. @xref{Blocks In Python}.
+@end defun
+
+@defun Compunit.static_block ()
+Return @code{gdb.Block} object representing 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 fa7dd192280..89315ebf866 100644
--- a/gdb/python/py-block.c
+++ b/gdb/python/py-block.c
@@ -218,6 +218,18 @@ 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 ());
+}
+
/* Implementation of gdb.Block.is_global (self) -> Boolean.
Returns True if this block object is a global block. */
@@ -556,6 +568,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..a2d7620f483
--- /dev/null
+++ b/gdb/python/py-compunit.c
@@ -0,0 +1,311 @@
+/* Python interface to compunits.
+
+ Copyright (C) 2025-2025 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 single-linked list, rooted in the objfile. This allows
+ invalidation of the underlying struct compunit_symtab when the objfile is
+ deleted. */
+ 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)
+ {
+ gdbpy_enter enter_py;
+
+ while (obj != nullptr)
+ {
+ compunit_object *next = obj->next;
+
+ obj->compunit = nullptr;
+ obj->next = nullptr;
+ Py_DECREF (obj);
+
+ 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;
+
+ CUPY_REQUIRE_VALID (self, compunit);
+
+ const struct blockvector *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;
+
+ CUPY_REQUIRE_VALID (self, compunit);
+
+ const struct blockvector *blockvector = compunit->blockvector ();
+ const struct block *block = blockvector->static_block ();
+
+ return block_to_block_object (block, compunit->objfile ());
+}
+
+/* Return a list of gdb.Symtab objects associated with the underlying
+ compunit. */
+
+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 ();
+}
+
+/* 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->next = cupy_objfile_data_key.get (compunit->objfile ());
+ cupy_objfile_data_key.set (compunit->objfile (), obj);
+
+ Py_INCREF (obj);
+}
+
+/* Return a new reference to gdb.Compunit Python object representing
+ COMPUNIT. Return NULL and set the Python error on failure. */
+PyObject *
+compunit_to_compunit_object (struct compunit_symtab *compunit)
+{
+ gdb_assert (compunit != nullptr);
+
+ compunit_object *compunit_obj
+ = cupy_objfile_data_key.get (compunit->objfile ());
+ while (compunit_obj != nullptr)
+ {
+ if (compunit_obj->compunit == compunit)
+ {
+ Py_INCREF (compunit_obj);
+ return (PyObject*)compunit_obj;
+ }
+ compunit_obj = compunit_obj->next;
+ }
+
+ compunit_obj = PyObject_New (compunit_object, &compunit_object_type);
+ if (compunit_obj)
+ set_compunit (compunit_obj, compunit);
+
+ return (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*/
+ 0, /*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 */
+ 0, /*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 1c6f5697302..b76f57e818d 100644
--- a/gdb/python/py-objfile.c
+++ b/gdb/python/py-objfile.c
@@ -544,6 +544,33 @@ 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;
+
+ self->objfile->expand_all_symtabs ();
+
+ for (struct compunit_symtab *compunit : self->objfile->compunits ())
+ {
+ PyObject *item = compunit_to_compunit_object (compunit);
+
+ if (item == nullptr
+ || PyList_Append (list.get (), item) == -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 +764,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 9f7bdb0f132..806a6efa4fd 100644
--- a/gdb/python/py-symtab.c
+++ b/gdb/python/py-symtab.c
@@ -149,6 +149,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 ());
+}
+
static PyObject *
stpy_fullname (PyObject *self, PyObject *args)
{
@@ -448,6 +460,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 7f4237eecc2..99e362c5221 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -548,6 +548,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);
+PyObject *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);
@@ -564,6 +566,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 ce3f7ce04c4..11c2de8fc6f 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-1.c b/gdb/testsuite/gdb.python/py-compunit-1.c
new file mode 100644
index 00000000000..f0f43fdd4ff
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-compunit-1.c
@@ -0,0 +1,24 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2025-2025 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/>. */
+
+int give_me_zero ();
+
+int
+main ()
+{
+ return give_me_zero ();
+}
diff --git a/gdb/testsuite/gdb.python/py-compunit-2.c b/gdb/testsuite/gdb.python/py-compunit-2.c
new file mode 100644
index 00000000000..f38a49ea30f
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-compunit-2.c
@@ -0,0 +1,28 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2025-2025 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/>. */
+
+int __attribute__ ((section (".text_give_me_one"))) __attribute__((noinline))
+give_me_one ()
+{
+ return 1;
+}
+
+int __attribute__ ((section (".text_give_me_zero")))
+give_me_zero ()
+{
+ return give_me_one() - 1;
+}
diff --git a/gdb/testsuite/gdb.python/py-compunit.exp b/gdb/testsuite/gdb.python/py-compunit.exp
new file mode 100644
index 00000000000..9e5c4dc6e03
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-compunit.exp
@@ -0,0 +1,83 @@
+# Copyright (C) 2025-2025 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
+
+# The test executable is compiled using two source files and uses custom
+# linker script. This is to force creation of two interleaved compunits
+# (py-compunit-1.c) with one function (main) and the other (py-compunit-2.c)
+# with two functions (get_me_one and get_me_zero) which are placed "before"
+# and "after" the main function.
+
+standard_testfile py-compunit-1.c py-compunit-2.c
+
+set ld_flags "-static -T${srcdir}/${subdir}/py-compunit.link"
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+ [list $srcfile $srcfile2] \
+ [list debug additional_flags=$ld_flags]]} {
+ 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(\"main\")" \
+ "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_py_test_silent_cmd "python compunit = objfile.compunits()\[0\]" \
+ "Get first compunit" 1
+gdb_test "python print (compunit.is_valid())" \
+ "True" \
+ "Compunit is valid"
+gdb_test "python print (compunit == compunit)" \
+ "True" \
+ "Compunits are comparable"
+gdb_test "python print (compunit is objfile.compunits()\[0\])" \
+ "True" \
+ "Compunits are identical"
+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 (compunit.is_valid())" "False" \
+"Get compunit validity after unload"
+gdb_py_test_silent_cmd "python compunit = None" \
+"Test compunit deallocation" 1
+gdb_test "python print (objfile.compunits())" "RuntimeError.*: Objfile no longer exists.*" \
+"Get objfile compunits after unload"
diff --git a/gdb/testsuite/gdb.python/py-compunit.link b/gdb/testsuite/gdb.python/py-compunit.link
new file mode 100644
index 00000000000..5b219acf916
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-compunit.link
@@ -0,0 +1,10 @@
+SECTIONS
+{
+ .text_give_me_zero 0x00080000 : { }
+} INSERT BEFORE .text;
+
+SECTIONS
+{
+ .text_give_me_one 0x00F00000 : { }
+}
+INSERT AFTER .text;
diff --git a/gdb/testsuite/gdb.python/py-symtab.exp b/gdb/testsuite/gdb.python/py-symtab.exp
index a4891f31288..4828a0a33b6 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"
@@ -89,6 +91,12 @@ gdb_test_multiple "python print (\"simple_struct\" in static_symbols)" \
}
}
}
+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 symtab identity
gdb_test "python print (symtab is symtab)"\
--
2.47.2
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC v5 08/18] gdb/python: allow instantiation of gdb.Objfile from Python
2025-06-23 16:09 [RFC v5 00/19] Add Python "JIT" API Jan Vrany
` (6 preceding siblings ...)
2025-06-23 16:10 ` [RFC v5 07/18] gdb/python: add gdb.Compunit Jan Vrany
@ 2025-06-23 16:10 ` Jan Vrany
2025-06-23 16:10 ` [RFC v5 09/18] gdb/python: add unlink () method to gdb.Objfile object Jan Vrany
` (9 subsequent siblings)
17 siblings, 0 replies; 31+ messages in thread
From: Jan Vrany @ 2025-06-23 16:10 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 3ef3699d355..ad9d65636b7 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -5793,6 +5793,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. 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 2aa11d3160d..250e16fa0d0 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 b76f57e818d..6403db583a7 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)
{
@@ -733,6 +812,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)
{
@@ -837,8 +942,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 99e362c5221..18b78a7d235 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -567,6 +567,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 8d1102896fa..055a35cea41 100644
--- a/gdb/testsuite/gdb.python/py-objfile.exp
+++ b/gdb/testsuite/gdb.python/py-objfile.exp
@@ -173,4 +173,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.47.2
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC v5 09/18] gdb/python: add unlink () method to gdb.Objfile object
2025-06-23 16:09 [RFC v5 00/19] Add Python "JIT" API Jan Vrany
` (7 preceding siblings ...)
2025-06-23 16:10 ` [RFC v5 08/18] gdb/python: allow instantiation of gdb.Objfile from Python Jan Vrany
@ 2025-06-23 16:10 ` Jan Vrany
2025-06-23 16:10 ` [RFC v5 10/18] gdb/python: allow instantiation of gdb.Compunit from Python Jan Vrany
` (8 subsequent siblings)
17 siblings, 0 replies; 31+ messages in thread
From: Jan Vrany @ 2025-06-23 16:10 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 ad9d65636b7..eac12bec4d3 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -5847,6 +5847,12 @@ Return a sequence of @code{gdb.Compunit} 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 6403db583a7..dc43e2c9e7d 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 *
@@ -873,6 +887,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 055a35cea41..1938350174d 100644
--- a/gdb/testsuite/gdb.python/py-objfile.exp
+++ b/gdb/testsuite/gdb.python/py-objfile.exp
@@ -210,3 +210,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.47.2
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC v5 10/18] gdb/python: allow instantiation of gdb.Compunit from Python
2025-06-23 16:09 [RFC v5 00/19] Add Python "JIT" API Jan Vrany
` (8 preceding siblings ...)
2025-06-23 16:10 ` [RFC v5 09/18] gdb/python: add unlink () method to gdb.Objfile object Jan Vrany
@ 2025-06-23 16:10 ` Jan Vrany
2025-06-23 16:10 ` [RFC v5 11/18] gdb/python: allow instantiation of gdb.Symtab " Jan Vrany
` (7 subsequent siblings)
17 siblings, 0 replies; 31+ messages in thread
From: Jan Vrany @ 2025-06-23 16:10 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/block.h | 9 ++
gdb/doc/python.texi | 16 ++++
gdb/python/py-compunit.c | 102 ++++++++++++++++++++++-
gdb/python/py-objfile.c | 12 +++
gdb/python/python-internal.h | 1 +
gdb/symtab.c | 64 ++++++++++++++
gdb/symtab.h | 6 ++
gdb/testsuite/gdb.python/py-compunit.exp | 47 +++++++++++
8 files changed, 256 insertions(+), 1 deletion(-)
diff --git a/gdb/block.h b/gdb/block.h
index 89ceeb4268b..3ea8878481e 100644
--- a/gdb/block.h
+++ b/gdb/block.h
@@ -24,6 +24,7 @@
#include "dictionary.h"
#include "gdbsupport/array-view.h"
#include "gdbsupport/next-iterator.h"
+#include "gdbsupport/range.h"
/* Opaque declarations. */
@@ -312,6 +313,14 @@ struct block : public allocate_on_obstack<block>
bool contains (const struct block *a, bool allow_nested = false) const;
+ /* Return true if this block's range overlap with [L, H) range. Return
+ false otherwise. */
+
+ bool overlaps (CORE_ADDR l, CORE_ADDR h) const
+ {
+ return ranges_overlap (l, h, start (), end ());
+ }
+
private:
/* Return the default entry-pc of this block. The default is the address
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index eac12bec4d3..ffa78c0d859 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -6716,6 +6716,22 @@ The sequence of @code{gdb.Symtab} objects 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 a2d7620f483..5346663af19 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"
@@ -198,6 +199,105 @@ set_compunit (compunit_object *obj, struct compunit_symtab *compunit)
Py_INCREF (obj);
}
+/* 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 a 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 compunit does not overlap any other existing
+ existing compunit. We have to make sure that all possibly overlapping
+ compunits are fully expanded before, though. */
+
+ for (struct objfile *of : objf->pspace ()->objfiles_safe ())
+ {
+ if (of->has_unexpanded_symtabs ())
+ of->expand_symtabs_maybe_overlapping (start, end);
+
+ for (const compunit_symtab *cu : of->compunits ())
+ {
+ if (cu->maybe_overlaps (start, end))
+ {
+ PyErr_Format (PyExc_ValueError,
+ _("The start-end range may overlap 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. */
PyObject *
@@ -305,7 +405,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 dc43e2c9e7d..170a440a0dd 100644
--- a/gdb/python/py-objfile.c
+++ b/gdb/python/py-objfile.c
@@ -826,6 +826,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 18b78a7d235..7daa58516f1 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -568,6 +568,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/symtab.c b/gdb/symtab.c
index 506e21eeac4..ba77c89573e 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -75,6 +75,7 @@
#include "gdbsupport/common-utils.h"
#include <optional>
#include "gdbsupport/unordered_set.h"
+#include "gdbsupport/range.h"
/* Forward declarations for local functions. */
@@ -537,6 +538,69 @@ compunit_symtab::maybe_contains (CORE_ADDR addr) const
/* See symtab.h. */
+bool
+compunit_symtab::maybe_overlaps (CORE_ADDR start, CORE_ADDR end) const
+{
+ if (blockvector ()->global_block ()->overlaps (start, end))
+ {
+ const addrmap_fixed *map = blockvector ()->map ();
+ if (map != nullptr)
+ {
+ CORE_ADDR range_start = 0;
+ auto fn = [&](CORE_ADDR addr, const void* b) -> int
+ {
+ if (range_start != 0)
+ {
+ /* We're currently "inside" a range. This transition means
+ that either:
+
+ (i) The current range ends (b == nullptr). In that case
+ check for overlap and if there's an overlap, return 1
+ and finish.
+
+ (ii) Or the range continues into another block. In that
+ case, just continue. */
+
+ if (b == nullptr)
+ {
+ CORE_ADDR range_end = addr;
+ bool overlaps = ranges_overlap (start, end,
+ range_start, range_end);
+ range_start = 0;
+ return overlaps;
+ }
+ else
+ {
+ return 0; /* continue iterating */
+ }
+ }
+ else
+ {
+ /* We're "outside" the range. This transition means that
+ either:
+
+ (i) This is a start of a new range (b != nullptr). In
+ this case, just note the start address (which also
+ indicates we're "inside" a range from now on).
+ (ii) This the very a beggining of address space. In tha
+ case do nothing. */
+ if (b != nullptr)
+ {
+ range_start = addr;
+ }
+ return 0; /* continue iterating */
+ }
+
+ };
+ return map->foreach (fn);
+ }
+ return true;
+ }
+ return false;
+}
+
+/* See symtab.h. */
+
void
compunit_symtab::finalize ()
{
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 7f084cef691..e55b06048e3 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1967,6 +1967,12 @@ struct compunit_symtab
definitely is not. */
bool maybe_contains (CORE_ADDR addr) const;
+ /* True, if given address range [START, END) may overlap with
+ addresses covered by this compunit. Return false if given
+ range definitely does not overlap. */
+
+ bool maybe_overlaps (CORE_ADDR start, CORE_ADDR end) const;
+
/* Unordered chain of all compunit symtabs of this objfile. */
struct compunit_symtab *next;
diff --git a/gdb/testsuite/gdb.python/py-compunit.exp b/gdb/testsuite/gdb.python/py-compunit.exp
index 9e5c4dc6e03..9f8b4cbe453 100644
--- a/gdb/testsuite/gdb.python/py-compunit.exp
+++ b/gdb/testsuite/gdb.python/py-compunit.exp
@@ -71,6 +71,50 @@ 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 compunit5 passing overlapping global block range"
+
+gdb_py_test_silent_cmd "python cu6 = gdb.Compunit(\"dynamic6\", objfile, 0x00EFFF00, 0x00EFFF0A)" \
+ "Create compunit6 'inside a hole' of existing compunit spanning over multiple disjoint ranges" 1
+
+gdb_test "python cu7 = gdb.Compunit(\"dynamic7\", objfile, 0x00F00000, 0x00F0000F)" \
+ "ValueError.*:.*" \
+ "Create compunit7 overlapping one of the existing compunit ranges"
+
gdb_unload "unload 1"
gdb_test "python print (objfile.is_valid())" "False" \
@@ -81,3 +125,6 @@ gdb_py_test_silent_cmd "python compunit = None" \
"Test compunit deallocation" 1
gdb_test "python print (objfile.compunits())" "RuntimeError.*: Objfile no longer exists.*" \
"Get objfile compunits after unload"
+gdb_test "python cu8 = gdb.Compunit(\"dynamic8\", objfile, 1000, 1100)" \
+ "TypeError.*: The objfile argument is not a valid gdb.Objfile object.*" \
+ "Create compunit8 passing invalid objfile"
--
2.47.2
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC v5 11/18] gdb/python: allow instantiation of gdb.Symtab from Python
2025-06-23 16:09 [RFC v5 00/19] Add Python "JIT" API Jan Vrany
` (9 preceding siblings ...)
2025-06-23 16:10 ` [RFC v5 10/18] gdb/python: allow instantiation of gdb.Compunit from Python Jan Vrany
@ 2025-06-23 16:10 ` Jan Vrany
2025-06-23 16:10 ` [RFC v5 12/18] gdb/python: allow instantiation of gdb.Block " Jan Vrany
` (6 subsequent siblings)
17 siblings, 0 replies; 31+ messages in thread
From: Jan Vrany @ 2025-06-23 16:10 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 | 14 +++++++
3 files changed, 69 insertions(+), 2 deletions(-)
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index ffa78c0d859..65e243074ff 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -6650,6 +6650,10 @@ The @code{gdb.Compunit} this symbol table belongs to.
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 806a6efa4fd..2faeb963ea6 100644
--- a/gdb/python/py-symtab.c
+++ b/gdb/python/py-symtab.c
@@ -92,6 +92,8 @@ static const gdbpy_registry<gdbpy_tracking_registry_storage<sal_object,
} \
} while (0)
+static void set_symtab (symtab_object *obj, struct symtab *symtab);
+
static PyObject *
stpy_str (PyObject *self)
{
@@ -235,6 +237,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)
{
@@ -438,7 +480,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;
@@ -514,7 +555,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 4828a0a33b6..fb1310da480 100644
--- a/gdb/testsuite/gdb.python/py-symtab.exp
+++ b/gdb/testsuite/gdb.python/py-symtab.exp
@@ -126,6 +126,16 @@ gdb_test "python print (symtab != \"xxx\")"\
"True" \
"test symtab non-equality with non-symtab"
+# 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
@@ -136,3 +146,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.47.2
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC v5 12/18] gdb/python: allow instantiation of gdb.Block from Python
2025-06-23 16:09 [RFC v5 00/19] Add Python "JIT" API Jan Vrany
` (10 preceding siblings ...)
2025-06-23 16:10 ` [RFC v5 11/18] gdb/python: allow instantiation of gdb.Symtab " Jan Vrany
@ 2025-06-23 16:10 ` Jan Vrany
2025-06-23 16:10 ` [RFC v5 13/18] gdb/python: allow instantiation of gdb.Symbol " Jan Vrany
` (5 subsequent siblings)
17 siblings, 0 replies; 31+ messages in thread
From: Jan Vrany @ 2025-06-23 16:10 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 65e243074ff..7f50615d151 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -6174,6 +6174,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 89315ebf866..43b15b6013e 100644
--- a/gdb/python/py-block.c
+++ b/gdb/python/py-block.c
@@ -344,6 +344,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 (each->overlaps (start, 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 *
@@ -535,7 +635,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;
@@ -615,7 +714,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 11c2de8fc6f..afa306be00f 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.47.2
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC v5 13/18] gdb/python: allow instantiation of gdb.Symbol from Python
2025-06-23 16:09 [RFC v5 00/19] Add Python "JIT" API Jan Vrany
` (11 preceding siblings ...)
2025-06-23 16:10 ` [RFC v5 12/18] gdb/python: allow instantiation of gdb.Block " Jan Vrany
@ 2025-06-23 16:10 ` Jan Vrany
2025-06-23 16:10 ` [RFC v5 14/18] gdb/python: add add_symbol () method to gdb.Block Jan Vrany
` (4 subsequent siblings)
17 siblings, 0 replies; 31+ messages in thread
From: Jan Vrany @ 2025-06-23 16:10 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 7f50615d151..709ef7c9d1a 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -6420,6 +6420,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 6d5dd588f91..f65620ae400 100644
--- a/gdb/objfiles.c
+++ b/gdb/objfiles.c
@@ -1251,3 +1251,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 cadb4525782..606928ec08b 100644
--- a/gdb/objfiles.h
+++ b/gdb/objfiles.h
@@ -665,6 +665,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 3028a307205..16bc8efca32 100644
--- a/gdb/python/py-symbol.c
+++ b/gdb/python/py-symbol.c
@@ -398,6 +398,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
@@ -773,5 +902,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 55cdebe5124..12c6268aace 100644
--- a/gdb/testsuite/gdb.python/py-symbol.exp
+++ b/gdb/testsuite/gdb.python/py-symbol.exp
@@ -212,6 +212,19 @@ if { [is_remote host] } {
}
gdb_test "python print (t\[0\].symtab)" "${py_symbol_c}" "get symtab"
+# 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.47.2
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC v5 14/18] gdb/python: add add_symbol () method to gdb.Block
2025-06-23 16:09 [RFC v5 00/19] Add Python "JIT" API Jan Vrany
` (12 preceding siblings ...)
2025-06-23 16:10 ` [RFC v5 13/18] gdb/python: allow instantiation of gdb.Symbol " Jan Vrany
@ 2025-06-23 16:10 ` Jan Vrany
2025-08-29 14:10 ` Andrew Burgess
2025-06-23 16:10 ` [RFC v5 15/18] gdb/python: add more attributes to gdb.LinetableEntry objects Jan Vrany
` (3 subsequent siblings)
17 siblings, 1 reply; 31+ messages in thread
From: Jan Vrany @ 2025-06-23 16:10 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 709ef7c9d1a..15f851e5fd0 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -6180,6 +6180,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 43b15b6013e..7926808d4db 100644
--- a/gdb/python/py-block.c
+++ b/gdb/python/py-block.c
@@ -263,6 +263,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. */
@@ -360,7 +397,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;
@@ -400,9 +437,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)
{
@@ -417,11 +454,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);
@@ -653,6 +690,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 afa306be00f..b1cb93ca718 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.47.2
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC v5 15/18] gdb/python: add more attributes to gdb.LinetableEntry objects
2025-06-23 16:09 [RFC v5 00/19] Add Python "JIT" API Jan Vrany
` (13 preceding siblings ...)
2025-06-23 16:10 ` [RFC v5 14/18] gdb/python: add add_symbol () method to gdb.Block Jan Vrany
@ 2025-06-23 16:10 ` Jan Vrany
2025-08-29 14:00 ` Andrew Burgess
2025-06-23 16:10 ` [RFC v5 16/18] gdb/python: allow instantiation of gdb.LineTableEntry objects Jan Vrany
` (2 subsequent siblings)
17 siblings, 1 reply; 31+ messages in thread
From: Jan Vrany @ 2025-06-23 16:10 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 15f851e5fd0..7c3a87cdaad 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -6822,6 +6822,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 90cba09c88e..5eb40bf3c5b 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 d27d16edc5d..40afd8c400e 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.47.2
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC v5 16/18] gdb/python: allow instantiation of gdb.LineTableEntry objects
2025-06-23 16:09 [RFC v5 00/19] Add Python "JIT" API Jan Vrany
` (14 preceding siblings ...)
2025-06-23 16:10 ` [RFC v5 15/18] gdb/python: add more attributes to gdb.LinetableEntry objects Jan Vrany
@ 2025-06-23 16:10 ` Jan Vrany
2025-06-23 16:10 ` [RFC v5 17/18] gdb/python: allow instantiation of gdb.LineTable objects Jan Vrany
2025-06-23 16:10 ` [RFC v5 18/18] gdb/python: add section in documentation on implementing JIT interface Jan Vrany
17 siblings, 0 replies; 31+ messages in thread
From: Jan Vrany @ 2025-06-23 16:10 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 7c3a87cdaad..2b33cd32201 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -6839,6 +6839,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 5eb40bf3c5b..57d1a74bf78 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 40afd8c400e..cf01a694070 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.47.2
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC v5 17/18] gdb/python: allow instantiation of gdb.LineTable objects
2025-06-23 16:09 [RFC v5 00/19] Add Python "JIT" API Jan Vrany
` (15 preceding siblings ...)
2025-06-23 16:10 ` [RFC v5 16/18] gdb/python: allow instantiation of gdb.LineTableEntry objects Jan Vrany
@ 2025-06-23 16:10 ` Jan Vrany
2025-06-23 16:10 ` [RFC v5 18/18] gdb/python: add section in documentation on implementing JIT interface Jan Vrany
17 siblings, 0 replies; 31+ messages in thread
From: Jan Vrany @ 2025-06-23 16:10 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 2b33cd32201..768ca8ba750 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -6876,6 +6876,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 57d1a74bf78..d4b1f86614a 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 cf01a694070..057a58aa200 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.47.2
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC v5 18/18] gdb/python: add section in documentation on implementing JIT interface
2025-06-23 16:09 [RFC v5 00/19] Add Python "JIT" API Jan Vrany
` (16 preceding siblings ...)
2025-06-23 16:10 ` [RFC v5 17/18] gdb/python: allow instantiation of gdb.LineTable objects Jan Vrany
@ 2025-06-23 16:10 ` Jan Vrany
17 siblings, 0 replies; 31+ messages in thread
From: Jan Vrany @ 2025-06-23 16:10 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 | 118 +++++++++++++++++++++++++++
6 files changed, 364 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 8e381329f9a..187808564ed 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -335,6 +335,10 @@ vFile:stat
** 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 4ef640698bd..b298494cc7c 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -40024,7 +40024,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 768ca8ba750..bd0099fd9fb 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -234,6 +234,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
@@ -8800,6 +8801,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()
+
+ # ...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..fde6d732760
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-jit.c
@@ -0,0 +1,61 @@
+/* Copyright (C) 2025-2025 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..09b550bbf36
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-jit.exp
@@ -0,0 +1,57 @@
+# Copyright (C) 2025-2025 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..efef3e9c19a
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-jit.py
@@ -0,0 +1,118 @@
+# Copyright (C) 2025-2025 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.47.2
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [RFC v5 01/18] gdb: introduce expand_symtabs_maybe_overlapping
2025-06-23 16:09 ` [RFC v5 01/18] gdb: introduce expand_symtabs_maybe_overlapping Jan Vrany
@ 2025-06-24 15:22 ` Tom Tromey
2025-06-26 15:05 ` Jan Vraný
0 siblings, 1 reply; 31+ messages in thread
From: Tom Tromey @ 2025-06-24 15:22 UTC (permalink / raw)
To: Jan Vrany; +Cc: gdb-patches
>>>>> "Jan" == Jan Vrany <jan.vrany@labware.com> writes:
Jan> This commit introduces expand_symtabs_maybe_overlapping, a new "quick
Jan> symbol function". This will be used later by Python API to create new
Jan> compunits to ensure to-be-created compunit does not overlap with
Jan> existing one.
Jan> For DWARF2 base index functions, all compunits are expanded
Jan> unconditionally since there seem to be no way to extract unexpanded
Jan> compunit's conservative address range.
This is extremely expensive, so I don't think it's a good approach.
You can probably call cooked_index::lookup to see if an address is
covered:
/* Look up ADDR in the address map, and return either the
corresponding CU, or nullptr if the address could not be
found. */
dwarf2_per_cu *lookup (unrelocated_addr addr) override;
Tom
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [RFC v5 04/18] gdb: introduce new function create_function_type
2025-06-23 16:09 ` [RFC v5 04/18] gdb: introduce new function create_function_type Jan Vrany
@ 2025-06-24 15:29 ` Tom Tromey
2025-06-26 11:12 ` Jan Vraný
0 siblings, 1 reply; 31+ messages in thread
From: Tom Tromey @ 2025-06-24 15:29 UTC (permalink / raw)
To: Jan Vrany; +Cc: gdb-patches
>>>>> "Jan" == Jan Vrany <jan.vrany@labware.com> writes:
Jan> - else if (check_typedef (param_types[nparams - 1])->code ()
Jan> - == TYPE_CODE_VOID)
Jan> + else if ((param_types[nparams - 1])->is_void ())
Jan> {
I don't see is_void in the tree and I don't remember it from patches 1-3.
Anyway the main question here is whether this drops a check_typedef.
Tom
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [RFC v5 05/18] gdb/python: add function () method to gdb.Type object
2025-06-23 16:10 ` [RFC v5 05/18] gdb/python: add function () method to gdb.Type object Jan Vrany
@ 2025-06-24 16:11 ` Tom Tromey
2025-06-26 11:13 ` Jan Vraný
0 siblings, 1 reply; 31+ messages in thread
From: Tom Tromey @ 2025-06-24 16:11 UTC (permalink / raw)
To: Jan Vrany; +Cc: gdb-patches, Eli Zaretskii
>>>>> "Jan" == Jan Vrany <jan.vrany@labware.com> writes:
Jan> +/* See gdbtypes.h. */
Jan> +
Jan> +bool
Jan> +type::is_void ()
Jan> +{
Jan> + return check_typedef (this)->code () == TYPE_CODE_VOID;
Jan> +}
This should be moved to the earlier patch that calls it.
Jan> +const std::string
Jan> +type::printable_name () const
Jan> +{
This isn't called in this patch but I don't think it really matters as
long as it's used eventually.
Jan> + string_file thetype;
Jan> +
Jan> + current_language->print_type (const_cast<type*>(this), "",
Space before "(this)"
Jan> + param_types[i] = type_object_to_type (param_type_obj);
Jan> + if (param_types[i] == nullptr)
Jan> + {
Jan> + PyErr_Format (PyExc_TypeError,
Jan> + _("Argument at index %d is %s, not a gdb.Type "
Jan> + "object."),
Jan> + i, Py_TYPE (param_type_obj)->tp_name);
Jan> + return nullptr;
It's kind of lame that we don't have a type_object_to_type_checked
variant.
Jan> + if (param_types[i]->is_void ())
Can check_typedef throw an exception? If so this must be wrapped in a
try/catch. That's a good practice for all calls into gdb anyway,
because things like this may change over time.
Jan> + if (kw != nullptr)
Jan> + {
Jan> + if (!PyDict_Check (kw))
Jan> + {
Jan> + PyErr_Format (PyExc_ValueError,
Jan> + _("Arguments is %s, not a dict."),
Jan> + Py_TYPE (args)->tp_name);
Jan> + return nullptr;
Jan> + }
Jan> +
This code should probably call PyArg_ValidateKeywordArguments.
I don't know whether that includes the PyDict_Check on its own.
Jan> + /* Copy all objfile-owned types to arch. This way user does not
Jan> + need to care about mixing types from different objfiles and
Jan> + architectures and we do not need to expose this implementation
Jan> + detail to the user. */
Jan> +
Jan> + copied_types_hash_t copied_types;
Jan> + return_type = copy_type_recursive (return_type, copied_types);
This whole block should probably be wrapped in a try/catch.
Jan> + function_type = lookup_function_type_with_arguments
Jan> + (return_type, param_types.size (), param_types.data ());
May be mildly nicer to use an array_view here.
Jan> + gdb_test "python print(repr(int_t.function(varargs=True)))" \
Jan> + "<gdb.Type code=TYPE_CODE_FUNC name=int \\(void\\)>"
Jan> +
Jan> + gdb_test "python print(repr(int_t.function(varargs=False)))" \
Jan> + "<gdb.Type code=TYPE_CODE_FUNC name=int \\(void\\)>"
Seems weird that these print the same thing.
Tom
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [RFC v5 04/18] gdb: introduce new function create_function_type
2025-06-24 15:29 ` Tom Tromey
@ 2025-06-26 11:12 ` Jan Vraný
2025-06-27 14:21 ` Tom Tromey
0 siblings, 1 reply; 31+ messages in thread
From: Jan Vraný @ 2025-06-26 11:12 UTC (permalink / raw)
To: gdb-patches; +Cc: tom
On Tue, 2025-06-24 at 09:29 -0600, Tom Tromey wrote:
> > > > > > "Jan" == Jan Vrany <jan.vrany@labware.com> writes:
>
> Jan> - else if (check_typedef (param_types[nparams - 1])->code
> ()
> Jan> - == TYPE_CODE_VOID)
> Jan> + else if ((param_types[nparams - 1])->is_void ())
> Jan> {
>
> I don't see is_void in the tree and I don't remember it from patches
> 1-3.
>
> Anyway the main question here is whether this drops a check_typedef.
>
> Tom
Ah, yes, I forgot to move that change to the following commit
("gdb/python: add function () method to gdb.Type object").
is_void() does drop the check_typedef().
Thanks!
Jan
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [RFC v5 05/18] gdb/python: add function () method to gdb.Type object
2025-06-24 16:11 ` Tom Tromey
@ 2025-06-26 11:13 ` Jan Vraný
0 siblings, 0 replies; 31+ messages in thread
From: Jan Vraný @ 2025-06-26 11:13 UTC (permalink / raw)
To: gdb-patches; +Cc: tom
On Tue, 2025-06-24 at 10:11 -0600, Tom Tromey wrote:
> > > > > > "Jan" == Jan Vrany <jan.vrany@labware.com> writes:
>
> Jan> +/* See gdbtypes.h. */
> Jan> +
> Jan> +bool
> Jan> +type::is_void ()
> Jan> +{
> Jan> + return check_typedef (this)->code () == TYPE_CODE_VOID;
> Jan> +}
>
> This should be moved to the earlier patch that calls it.
>
I did it the other way around since this commit actually makes
use of it in typy_function().
> Jan> +const std::string
> Jan> +type::printable_name () const
> Jan> +{
>
> This isn't called in this patch but I don't think it really matters
> as
> long as it's used eventually.
The reason for this was to provide nicer error message in earlier
versions of this patch but it is not used now that this copies objfile-
owned types to the arch. So I'll drop it for now.
>
> Jan> + string_file thetype;
> Jan> +
> Jan> + current_language->print_type (const_cast<type*>(this), "",
>
> Space before "(this)"
>
> Jan> + param_types[i] = type_object_to_type (param_type_obj);
> Jan> + if (param_types[i] == nullptr)
> Jan> + {
> Jan> + PyErr_Format (PyExc_TypeError,
> Jan> + _("Argument at index %d is %s, not a gdb.Type "
> Jan> + "object."),
> Jan> + i, Py_TYPE (param_type_obj)->tp_name);
> Jan> + return nullptr;
>
> It's kind of lame that we don't have a type_object_to_type_checked
> variant.
I'm not sure what do you mean here, can you please elaborate a bit?
>
> Jan> + if (param_types[i]->is_void ())
>
> Can check_typedef throw an exception? If so this must be wrapped in
> a
> try/catch. That's a good practice for all calls into gdb anyway,
> because things like this may change over time.
>
> Jan> + if (kw != nullptr)
> Jan> + {
> Jan> + if (!PyDict_Check (kw))
> Jan> + {
> Jan> + PyErr_Format (PyExc_ValueError,
> Jan> + _("Arguments is %s, not a dict."),
> Jan> + Py_TYPE (args)->tp_name);
> Jan> + return nullptr;
> Jan> + }
> Jan> +
>
> This code should probably call PyArg_ValidateKeywordArguments.
> I don't know whether that includes the PyDict_Check on its own.
Looking at the python source code, it does PyDict_Check().
I changed the code to make it little, makes code a little shorter.
>
> Jan> + /* Copy all objfile-owned types to arch. This way user does
> not
> Jan> + need to care about mixing types from different objfiles
> and
> Jan> + architectures and we do not need to expose this
> implementation
> Jan> + detail to the user. */
> Jan> +
> Jan> + copied_types_hash_t copied_types;
> Jan> + return_type = copy_type_recursive (return_type,
> copied_types);
>
> This whole block should probably be wrapped in a try/catch.
>
> Jan> + function_type = lookup_function_type_with_arguments
> Jan> + (return_type, param_types.size (), param_types.data
> ());
>
> May be mildly nicer to use an array_view here.
>
Yes. This is pre-existing function so I did not changed that. Looking
at users, all use std::vector to store types so I can change the
function to just take a vector.
> Jan> + gdb_test "python print(repr(int_t.function(varargs=True)))" \
> Jan> + "<gdb.Type code=TYPE_CODE_FUNC name=int \\(void\\)>"
> Jan> +
> Jan> + gdb_test "python print(repr(int_t.function(varargs=False)))"
> \
> Jan> + "<gdb.Type code=TYPE_CODE_FUNC name=int \\(void\\)>"
>
> Seems weird that these print the same thing.
Yes, this is perhaps bug in type printing. See #32452
https://sourceware.org/bugzilla/show_bug.cgi?id=32452
and also my earlier conversation with Andrew
https://inbox.sourceware.org/gdb-patches/bae30cf2e19b236f55ae38cf7dcf4f17dad2c4be.camel@labware.com/
Below you may find updated patch. I'll resend the whole series once
more updates/comments accumulate. Thanks a lot! Jan
-- >8 --
From fe166a218161c07e0aa9cec9059a47280efff07e Mon Sep 17 00:00:00 2001
From: Jan Vrany <jan.vrany@labware.com>
Date: Wed, 25 Jun 2025 19:50:13 +0100
Subject: [PATCH 06/19] gdb/python: add function () method to gdb.Type
object
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.
Unlike previous version, this commit copies all objfile-owned types
passed to gdb.Type.function to make them arch-owned. This way we do not
have to expose implementation details of type ownership to user.
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
gdb/NEWS | 3 +
gdb/doc/python.texi | 11 +++
gdb/gdbtypes.c | 11 ++-
gdb/gdbtypes.h | 3 +
gdb/python/py-type.c | 127 +++++++++++++++++++++++++++
gdb/testsuite/gdb.python/py-type.exp | 36 ++++++++
6 files changed, 189 insertions(+), 2 deletions(-)
diff --git a/gdb/NEWS b/gdb/NEWS
index 6c8a008d39d..e8e32a5dd5c 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -330,6 +330,9 @@ vFile:stat
** 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 6fa22851f6a..633526dc875 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -1594,6 +1594,17 @@ Return a new @code{gdb.Type} object which
represents a pointer to this
type.
@end defun
+@defun Type.function (@r{[}param_type@dots{}@r{]}, @r{[}varargs@r{]})
+Return a new @code{gdb.Type} object which represents a type of
function
+returning this type. Returned function type is always marked as
prototyped.
+
+@var{param_type@dots{}} positional-only arguments specify parameter
types.
+Passing @code{void} type is not allowed.
+
+The optional @var{varargs} keyword-only argument specifies whether to
create
+vararg function type. Defaults to @code{False}.
+@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/gdbtypes.c b/gdb/gdbtypes.c
index ac2dc52f555..964a0360257 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -45,6 +45,7 @@
#include "rust-lang.h"
#include "ada-lang.h"
#include "extract-store-integer.h"
+#include "typeprint.h"
/* The value of an invalid conversion badness. */
#define INVALID_CONVERSION 100
@@ -581,8 +582,7 @@ create_function_type (type_allocator &alloc,
--nparams;
fn->set_has_varargs (true);
}
- else if (check_typedef (param_types[nparams - 1])->code ()
- == TYPE_CODE_VOID)
+ else if ((param_types[nparams - 1])->is_void ())
{
--nparams;
/* Caller should have ensured this. */
@@ -5976,6 +5976,13 @@ type::is_array_like ()
return defn->is_array_like (this);
}
+/* See gdbtypes.h. */
+
+bool
+type::is_void ()
+{
+ return check_typedef (this)->code () == TYPE_CODE_VOID;
+}
\f
static const registry<gdbarch>::key<struct builtin_type>
gdbtypes_data;
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index ba216c6db54..327c29bd3ff 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -1526,6 +1526,9 @@ struct type
representations of arrays by the type's language. */
bool is_array_like ();
+ /* Return true if this type is "void". Follows typedefs. */
+ bool is_void ();
+
/* Return the language that this type came from. */
enum language language () const
{ return main_type->m_lang; }
diff --git a/gdb/python/py-type.c b/gdb/python/py-type.c
index c546aa7536a..c2f7def5dfa 100644
--- a/gdb/python/py-type.c
+++ b/gdb/python/py-type.c
@@ -768,6 +768,130 @@ typy_unqualified (PyObject *self, PyObject *args)
return type_to_type_object (type);
}
+/* Return a function type. */
+
+static PyObject *
+typy_function (PyObject *self, PyObject *args, PyObject *kw)
+{
+ struct type *return_type = ((type_object *) self)->type;
+
+ /* Process arguments. We cannot simply use
PyArg_ParseTupleAndKeywords
+ because this method take variable number of arguments. */
+ if (args == nullptr || !PyTuple_Check (args))
+ {
+ PyErr_Format (PyExc_ValueError,
+ _("Arguments is %s, not a tuple."),
+ Py_TYPE (args)->tp_name);
+ return nullptr;
+ }
+ int nparam_types = PyTuple_Size (args);
+ if (nparam_types == -1)
+ {
+ /* Can this really happen? At this point we _know_ that
+ ARGS is a tuple (or subclass). */
+ PyErr_Format (PyExc_ValueError,
+ _("Failed retrieve number of parameters."));
+ return nullptr;
+ }
+ std::vector<struct type *> param_types (nparam_types);
+ for (int i = 0; i < nparam_types; i++)
+ {
+ PyObject *param_type_obj = PySequence_GetItem (args, i);
+
+ if (param_type_obj == nullptr)
+ {
+ PyErr_Format (PyExc_ValueError,
+ _("Failed to retrieve parameter at index %d."), i);
+ return nullptr;
+ }
+ else
+ {
+ param_types[i] = type_object_to_type (param_type_obj);
+ if (param_types[i] == nullptr)
+ {
+ PyErr_Format (PyExc_TypeError,
+ _("Argument at index %d is %s, not a gdb.Type "
+ "object."),
+ i, Py_TYPE (param_type_obj)->tp_name);
+ return nullptr;
+ }
+ try
+ {
+ if (param_types[i]->is_void ())
+ {
+ PyErr_Format (PyExc_ValueError,
+ _("Argument at index %d is a void type but "
+ "void as parameter type is not allowed."),
+ i);
+ return nullptr;
+ }
+ }
+ catch (const gdb_exception &except)
+ {
+ return gdbpy_handle_gdb_exception (nullptr, except);
+ }
+ }
+ }
+
+ if (kw != nullptr)
+ {
+ if (!PyArg_ValidateKeywordArguments (kw))
+ return nullptr;
+
+ PyObject *key, *value;
+ Py_ssize_t pos = 0;
+ while (PyDict_Next (kw, &pos, &key, &value))
+ {
+ if (PyUnicode_CompareWithASCIIString (key, "varargs") != 0)
+ {
+ PyErr_Format (PyExc_ValueError,
+ _("Invalid keyword argument \"%U\"."),
+ key);
+ return nullptr;
+ }
+ if (!PyBool_Check (value))
+ {
+ PyErr_Format (PyExc_ValueError,
+ _("Value of \"varargs\" argument is \"%s\", "
+ "not a bool."),
+ Py_TYPE (value)->tp_name);
+ return nullptr;
+ }
+ if (value == Py_True)
+ {
+ param_types.push_back (nullptr);
+ }
+ }
+ }
+
+ struct type *function_type = nullptr;
+ try
+ {
+ /* Copy all objfile-owned types to arch. This way user does not
+ need to care about mixing types from different objfiles and
+ architectures and we do not need to expose this implementation
+ detail to the user. */
+
+ copied_types_hash_t copied_types;
+ return_type = copy_type_recursive (return_type, copied_types);
+ for (int i = 0; i < param_types.size (); i++)
+ {
+ if (param_types[i] != nullptr)
+ param_types[i] = copy_type_recursive (param_types[i], copied_types);
+ }
+
+ function_type = lookup_function_type_with_arguments
+ (return_type, param_types.size (), param_types.data ());
+ function_type->set_is_prototyped (true);
+ }
+ catch (const gdb_exception &except)
+ {
+ return gdbpy_handle_gdb_exception (nullptr, except);
+ }
+
+ return type_to_type_object (function_type);
+}
+
/* Return the size of the type represented by SELF, in bytes. */
static PyObject *
typy_get_sizeof (PyObject *self, void *closure)
@@ -1630,6 +1754,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", (PyCFunction) typy_function, METH_VARARGS |
METH_KEYWORDS,
+ "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 0bc4556c653..876ef474a32 100644
--- a/gdb/testsuite/gdb.python/py-type.exp
+++ b/gdb/testsuite/gdb.python/py-type.exp
@@ -378,6 +378,42 @@ 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_no_output "python uu_t = gdb.parse_and_eval ('uu').type"
+
+ gdb_test "python print(repr(int_t.function()))" \
+ "<gdb.Type code=TYPE_CODE_FUNC name=int \\(void\\)>"
+
+ 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, varargs=True)))" \
+ "<gdb.Type code=TYPE_CODE_FUNC name=int \\(int, ...\\)>"
+
+ gdb_test "python print(repr(int_t.function(varargs=True)))" \
+ "<gdb.Type code=TYPE_CODE_FUNC name=int \\(void\\)>"
+
+ gdb_test "python print(repr(int_t.function(varargs=False)))" \
+ "<gdb.Type code=TYPE_CODE_FUNC name=int \\(void\\)>"
+
+ gdb_test "python print(repr(int_t.function(123)))" \
+ "TypeError.*:.*"
+
+ gdb_test "python
print(repr(int_t.function(gdb.lookup_type('void'))))" \
+ "ValueError.*:.*"
+
+ gdb_test "python print(repr(int_t.function(int_t,
gdb.lookup_type('void'))))" \
+ "ValueError.*:.*"
+
+ gdb_test "python print(repr(int_t.function(int_t,
varargs=\[1,2,3\])))" \
+ "ValueError.*:.*"
+
+ gdb_test "python print(repr(int_t.function(int_t, kw=False)))" \
+ "ValueError.*:.*"
+
+ gdb_test "python print(repr(int_t.function(gdb.lookup_type('void'),
varargs=True)))" \
+ "ValueError.*:.*"
+
set sint [get_sizeof int 0]
gdb_test "python
print(gdb.parse_and_eval('aligncheck').type.alignof)" \
$sint
--
2.47.2
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [RFC v5 01/18] gdb: introduce expand_symtabs_maybe_overlapping
2025-06-24 15:22 ` Tom Tromey
@ 2025-06-26 15:05 ` Jan Vraný
0 siblings, 0 replies; 31+ messages in thread
From: Jan Vraný @ 2025-06-26 15:05 UTC (permalink / raw)
To: gdb-patches; +Cc: tom
On Tue, 2025-06-24 at 09:22 -0600, Tom Tromey wrote:
> > > > > > "Jan" == Jan Vrany <jan.vrany@labware.com> writes:
>
> Jan> This commit introduces expand_symtabs_maybe_overlapping, a new
> "quick
> Jan> symbol function". This will be used later by Python API to
> create new
> Jan> compunits to ensure to-be-created compunit does not overlap with
> Jan> existing one.
>
> Jan> For DWARF2 base index functions, all compunits are expanded
> Jan> unconditionally since there seem to be no way to extract
> unexpanded
> Jan> compunit's conservative address range.
>
> This is extremely expensive, so I don't think it's a good approach.
>
> You can probably call cooked_index::lookup to see if an address is
> covered:
>
> /* Look up ADDR in the address map, and return either the
> corresponding CU, or nullptr if the address could not be
> found. */
> dwarf2_per_cu *lookup (unrelocated_addr addr) override;
Ah, I overlooked that. I have updated this patch (below) to
use m_addrmap in cooked_index_shard to find CUs that need
expanding.
Hopefully it would work, I'm not sure how to test this.
For dwarf2_base_index_functions it still expands all symtabs. Is
there a way to get memory ranges of CUs for
dwarf2_base_index_functions?
Thanks!
Jan
-- >8 --
From a8c2506bd37dab4a7bc63f6da4d7945da4714724 Mon Sep 17 00:00:00 2001
From: Jan Vrany <jan.vrany@labware.com>
Date: Mon, 23 Jun 2025 17:07:55 +0100
Subject: [PATCH 02/19] gdb: introduce expand_symtabs_maybe_overlapping
This commit introduces expand_symtabs_maybe_overlapping, a new "quick
symbol function". This will be used later by Python API to create new
compunits to ensure to-be-created compunit does not overlap with
existing one.
For DWARF2 base index functions, all compunits are expanded
unconditionally since there seem to be no way to extract unexpanded
compunit's conservative address range.
For "cooked index" functions, expand_symtabs_maybe_overlapping () makes
use of address map in cooked_index to find overlapping CUs and expands
only those. This required to export dw2_instantiate_symtab ().
---
gdb/dwarf2/cooked-index-shard.c | 34 ++++++++++++++++++++++++++++++++
gdb/dwarf2/cooked-index-shard.h | 5 +++++
gdb/dwarf2/cooked-index.c | 17 ++++++++++++++++
gdb/dwarf2/cooked-index.h | 24 ++++++++++++++++++++++
gdb/dwarf2/read.c | 18 ++++++++++++-----
gdb/dwarf2/read.h | 12 +++++++++++
gdb/objfiles.h | 3 +++
gdb/psymtab.c | 21 ++++++++++++++++++++
gdb/psymtab.h | 3 +++
gdb/quick-symbol.h | 5 +++++
gdb/symfile-debug.c | 16 +++++++++++++++
gdbsupport/range.h | 35 +++++++++++++++++++++++++++++++++
12 files changed, 188 insertions(+), 5 deletions(-)
create mode 100644 gdbsupport/range.h
diff --git a/gdb/dwarf2/cooked-index-shard.c b/gdb/dwarf2/cooked-index-
shard.c
index c717bbbf6c0..1ee6077dc20 100644
--- a/gdb/dwarf2/cooked-index-shard.c
+++ b/gdb/dwarf2/cooked-index-shard.c
@@ -329,3 +329,37 @@ cooked_index_shard::find (const std::string &name,
bool completing) const
? cooked_index_entry::COMPLETE
: cooked_index_entry::MATCH) }));
}
+
+/* See cooked-index-shard.h. */
+
+std::vector<dwarf2_per_cu *>
+cooked_index_shard::lookup_overlapping (unrelocated_addr start,
+ unrelocated_addr end)
+ {
+ std::vector<dwarf2_per_cu *> result;
+
+ CORE_ADDR start_addr = (CORE_ADDR)start;
+ CORE_ADDR end_addr = (CORE_ADDR)end;
+ CORE_ADDR prev_addr = 0;
+ void *prev_obj = nullptr;
+
+ if (m_addrmap != nullptr)
+ m_addrmap->foreach ([&] (CORE_ADDR curr_addr, void *curr_obj) -
> int
+ {
+ if (curr_addr >= end_addr)
+ return 1;
+
+ if (curr_addr != 0
+ && prev_obj != nullptr
+ && ranges_overlap (start_addr, end_addr, prev_addr,
curr_addr-1))
+ {
+ result.push_back (static_cast<dwarf2_per_cu *>
(prev_obj));
+ }
+ prev_addr = curr_addr;
+ prev_obj = curr_obj;
+
+ return 0;
+ });
+
+ return result;
+ }
diff --git a/gdb/dwarf2/cooked-index-shard.h b/gdb/dwarf2/cooked-index-
shard.h
index 925960b7ece..62244ac5d3e 100644
--- a/gdb/dwarf2/cooked-index-shard.h
+++ b/gdb/dwarf2/cooked-index-shard.h
@@ -26,6 +26,7 @@
#include "addrmap.h"
#include "gdbsupport/iterator-range.h"
#include "gdbsupport/string-set.h"
+#include "gdbsupport/range.h"
/* An index of interesting DIEs. This is "cooked", in contrast to a
mapped .debug_names or .gdb_index, which are "raw". An entry in
@@ -92,6 +93,10 @@ class cooked_index_shard
return (static_cast<dwarf2_per_cu *> (m_addrmap->find ((CORE_ADDR)
addr)));
}
+ /* Look up all CUs that overlap with range [START, END). */
+ std::vector<dwarf2_per_cu *> lookup_overlapping (unrelocated_addr
start,
+ unrelocated_addr
end);
+
/* Create a new cooked_index_entry and register it with this object.
Entries are owned by this object. The new item is returned. */
cooked_index_entry *create (sect_offset die_offset,
diff --git a/gdb/dwarf2/cooked-index.c b/gdb/dwarf2/cooked-index.c
index 7232f286a40..0770eb797f2 100644
--- a/gdb/dwarf2/cooked-index.c
+++ b/gdb/dwarf2/cooked-index.c
@@ -144,6 +144,23 @@ cooked_index::lookup (unrelocated_addr addr)
/* See cooked-index.h. */
+std::vector<dwarf2_per_cu *>
+cooked_index::lookup_overlapping (unrelocated_addr start,
+ unrelocated_addr end)
+{
+ /* Ensure that the address maps are ready. */
+ wait (cooked_state::MAIN_AVAILABLE, true);
+ std::vector<dwarf2_per_cu *> result;
+ for (const auto &shard : m_shards)
+ {
+ for (dwarf2_per_cu *cu : shard->lookup_overlapping (start, end))
+ result.push_back (cu);
+ }
+ return result;
+}
+
+/* See cooked-index.h. */
+
std::vector<const addrmap *>
cooked_index::get_addrmaps ()
{
diff --git a/gdb/dwarf2/cooked-index.h b/gdb/dwarf2/cooked-index.h
index 43b27232aec..605112f63fd 100644
--- a/gdb/dwarf2/cooked-index.h
+++ b/gdb/dwarf2/cooked-index.h
@@ -132,6 +132,10 @@ class cooked_index : public dwarf_scanner_base
found. */
dwarf2_per_cu *lookup (unrelocated_addr addr) override;
+ /* Look up all CUs that overlap with range <START, END). */
+ std::vector<dwarf2_per_cu *> lookup_overlapping (unrelocated_addr
start,
+ unrelocated_addr
end);
+
/* Return a new vector of all the addrmaps used by all the indexes
held by this object.
@@ -237,6 +241,26 @@ struct cooked_index_functions : public
dwarf2_base_index_functions
dwarf2_base_index_functions::expand_all_symtabs (objfile);
}
+ /* Cooked index's version of expand_symtabs_maybe_overlapping. See
its
+ definition in the definition of quick_symbol_functions in
symfile.h. */
+
+ void expand_symtabs_maybe_overlapping (struct objfile *objfile,
+ CORE_ADDR start, CORE_ADDR end) override
+ {
+ cooked_index *index = wait (objfile, true);
+
+ unrelocated_addr unrel_start
+ = unrelocated_addr (start - objfile->text_section_offset ());
+ unrelocated_addr unrel_end
+ = unrelocated_addr (end - objfile->text_section_offset ());
+
+ for (dwarf2_per_cu *cu
+ : index->lookup_overlapping (unrel_start, unrel_end))
+ {
+ dw2_instantiate_symtab (cu, get_dwarf2_per_objfile (objfile),
false);
+ }
+ }
+
bool expand_symtabs_matching
(struct objfile *objfile,
expand_symtabs_file_matcher file_matcher,
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 2f27b7cff4c..981b4ed7bcc 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -1660,12 +1660,9 @@ dw2_do_instantiate_symtab (dwarf2_per_cu
*per_cu,
per_objfile->age_comp_units ();
}
-/* Ensure that the symbols for PER_CU have been read in.
DWARF2_PER_OBJFILE is
- the per-objfile for which this symtab is instantiated.
-
- Returns the resulting symbol table. */
+/* See read.h. */
-static struct compunit_symtab *
+compunit_symtab *
dw2_instantiate_symtab (dwarf2_per_cu *per_cu, dwarf2_per_objfile
*per_objfile,
bool skip_partial)
{
@@ -1926,6 +1923,17 @@ dwarf2_base_index_functions::expand_all_symtabs
(struct objfile *objfile)
}
}
+/* DWARF2 index's version of expand_symtabs_maybe_overlapping. See
its definition
+ in the definition of quick_symbol_functions in symfile.h. */
+
+void
+dwarf2_base_index_functions::expand_symtabs_maybe_overlapping
+ (struct objfile *objfile, CORE_ADDR start, CORE_ADDR end)
+{
+ /* Simply expand all symtabs. I do not know how to extract (if at
all
+ possible) conservative address range from dwarf2_per_cu_data. */
+ expand_all_symtabs (objfile);
+}
/* See read.h. */
bool
diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
index a5cfb3144f7..a5cbc1b0ae0 100644
--- a/gdb/dwarf2/read.h
+++ b/gdb/dwarf2/read.h
@@ -1192,6 +1192,9 @@ struct dwarf2_base_index_functions : public
quick_symbol_functions
void expand_all_symtabs (struct objfile *objfile) override;
+ virtual void expand_symtabs_maybe_overlapping (struct objfile
*objfile,
+ CORE_ADDR start, CORE_ADDR end) override;
+
struct compunit_symtab *find_pc_sect_compunit_symtab
(struct objfile *objfile, bound_minimal_symbol msymbol,
CORE_ADDR pc, struct obj_section *section, int warn_if_readin)
@@ -1327,4 +1330,13 @@ extern int dwarf2_ranges_read (unsigned offset,
unrelocated_addr *low_return,
extern file_and_directory &find_file_and_directory (die_info *die,
dwarf2_cu *cu);
+/* Ensure that the symbols for PER_CU have been read in.
DWARF2_PER_OBJFILE is
+ the per-objfile for which this symtab is instantiated.
+
+ Returns the resulting symbol table. */
+
+extern compunit_symtab *
+dw2_instantiate_symtab (dwarf2_per_cu *per_cu, dwarf2_per_objfile
*per_objfile,
+ bool skip_partial);
+
#endif /* GDB_DWARF2_READ_H */
diff --git a/gdb/objfiles.h b/gdb/objfiles.h
index 4a3475862a9..0206b49e00c 100644
--- a/gdb/objfiles.h
+++ b/gdb/objfiles.h
@@ -597,6 +597,9 @@ struct objfile : intrusive_list_node<objfile>
code, e.g., DW_TAG_type_unit for dwarf debug info. */
void expand_symtabs_with_fullname (const char *fullname);
+ /* See quick_symbol_functions. */
+ void expand_symtabs_maybe_overlapping (CORE_ADDR start, CORE_ADDR
end);
+
/* See quick_symbol_functions. */
bool expand_symtabs_matching
(expand_symtabs_file_matcher file_matcher,
diff --git a/gdb/psymtab.c b/gdb/psymtab.c
index 986ef44f6c7..d40afd5877f 100644
--- a/gdb/psymtab.c
+++ b/gdb/psymtab.c
@@ -36,6 +36,7 @@
#include <algorithm>
#include <set>
#include "gdbsupport/buildargv.h"
+#include "gdbsupport/range.h"
static const struct partial_symbol *lookup_partial_symbol
(struct objfile *, struct partial_symtab *, const
lookup_name_info &,
@@ -731,6 +732,26 @@ psymbol_functions::expand_all_symtabs (struct
objfile *objfile)
psymtab_to_symtab (objfile, psymtab);
}
+/* Psymtab version of expand_symtabs_maybe_overlapping. See its
definition in
+ the definition of quick_symbol_functions in symfile.h. */
+
+void
+psymbol_functions::expand_symtabs_maybe_overlapping
+ (struct objfile *objfile, CORE_ADDR start, CORE_ADDR end)
+{
+ for (partial_symtab *psymtab : partial_symbols (objfile))
+ {
+ if (psymtab->text_low_valid && psymtab->text_high_valid)
+ {
+ CORE_ADDR text_low = psymtab->text_low (objfile);
+ CORE_ADDR text_high = psymtab->text_high (objfile);
+
+ if (ranges_overlap (start, end, text_low, text_high))
+ psymtab_to_symtab (objfile, psymtab);
+ }
+ }
+}
+
/* Psymtab version of map_symbol_filenames. See its definition in
the definition of quick_symbol_functions in symfile.h. */
diff --git a/gdb/psymtab.h b/gdb/psymtab.h
index ad0b2ff4234..8a8ea738bbd 100644
--- a/gdb/psymtab.h
+++ b/gdb/psymtab.h
@@ -626,6 +626,9 @@ struct psymbol_functions : public
quick_symbol_functions
void expand_all_symtabs (struct objfile *objfile) override;
+ virtual void expand_symtabs_maybe_overlapping (struct objfile
*objfile,
+ CORE_ADDR start, CORE_ADDR end) override;
+
bool expand_symtabs_matching
(struct objfile *objfile,
expand_symtabs_file_matcher file_matcher,
diff --git a/gdb/quick-symbol.h b/gdb/quick-symbol.h
index 9db1f1860cb..899f2f22239 100644
--- a/gdb/quick-symbol.h
+++ b/gdb/quick-symbol.h
@@ -130,6 +130,11 @@ struct quick_symbol_functions
/* Read all symbol tables associated with OBJFILE. */
virtual void expand_all_symtabs (struct objfile *objfile) = 0;
+ /* Read all symbol tables associated with OBJFILE which may overlap
+ with range [START, END). */
+ virtual void expand_symtabs_maybe_overlapping (struct objfile
*objfile,
+ CORE_ADDR start, CORE_ADDR end) = 0;
+
/* Expand all symbol tables in OBJFILE matching some criteria.
If LANG_MATCHER returns false, expansion of the symbol table may
be
diff --git a/gdb/symfile-debug.c b/gdb/symfile-debug.c
index 9c5ce85d26e..900d1bf90e2 100644
--- a/gdb/symfile-debug.c
+++ b/gdb/symfile-debug.c
@@ -343,6 +343,22 @@ objfile::expand_all_symtabs ()
iter->expand_all_symtabs (this);
}
+/* See objfiles.h. */
+
+void
+objfile::expand_symtabs_maybe_overlapping (CORE_ADDR start, CORE_ADDR
end)
+{
+ if (debug_symfile)
+ gdb_printf (gdb_stdlog,
+ "qf->expand_symtabs_maybe_overlapping (%s, %s, %s)\n",
+ objfile_debug_name (this),
+ hex_string (start),
+ hex_string (end));
+
+ for (const auto &iter : qf)
+ iter->expand_symtabs_maybe_overlapping (this, start, end);
+}
+
void
objfile::expand_symtabs_with_fullname (const char *fullname)
{
diff --git a/gdbsupport/range.h b/gdbsupport/range.h
new file mode 100644
index 00000000000..b665943cfd3
--- /dev/null
+++ b/gdbsupport/range.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2025-2025 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/>. */
+
+#ifndef GDBSUPPORT_RANGE_H
+#define GDBSUPPORT_RANGE_H
+
+/* Return true if given ranges [AL, AH) and [BL, BH) overlap. Return
false
+ otherwise. */
+
+template <typename T>
+bool ranges_overlap (T al, T ah, T bl, T bh)
+{
+ static_assert (std::is_integral<T>::value, "Integral type
required");
+
+ gdb_assert (al <= ah);
+ gdb_assert (bl <= bh);
+
+ return !(ah <= bl || bh <= al);
+}
+
+#endif /* GDBSUPPORT_RANGE_H */
--
2.47.2
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [RFC v5 04/18] gdb: introduce new function create_function_type
2025-06-26 11:12 ` Jan Vraný
@ 2025-06-27 14:21 ` Tom Tromey
2025-06-27 14:30 ` Jan Vraný
0 siblings, 1 reply; 31+ messages in thread
From: Tom Tromey @ 2025-06-27 14:21 UTC (permalink / raw)
To: Jan Vraný; +Cc: gdb-patches, tom
>> Anyway the main question here is whether this drops a check_typedef.
Jan> is_void() does drop the check_typedef().
That would be bad if it did, but I think when I looked at the patch, it
did not?
Tom
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [RFC v5 04/18] gdb: introduce new function create_function_type
2025-06-27 14:21 ` Tom Tromey
@ 2025-06-27 14:30 ` Jan Vraný
0 siblings, 0 replies; 31+ messages in thread
From: Jan Vraný @ 2025-06-27 14:30 UTC (permalink / raw)
To: tom; +Cc: gdb-patches
On Fri, 2025-06-27 at 08:21 -0600, Tom Tromey wrote:
> > > Anyway the main question here is whether this drops a
> > > check_typedef.
>
> Jan> is_void() does drop the check_typedef().
>
> That would be bad if it did, but I think when I looked at the patch,
> it
> did not?
Ah, stupid me! Of course I meant that it does *not* drop the typedef:
/* See gdbtypes.h. */
bool
type::is_void ()
{
return check_typedef (this)->code () == TYPE_CODE_VOID;
}
Sorry!
Jan
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [RFC v5 15/18] gdb/python: add more attributes to gdb.LinetableEntry objects
2025-06-23 16:10 ` [RFC v5 15/18] gdb/python: add more attributes to gdb.LinetableEntry objects Jan Vrany
@ 2025-08-29 14:00 ` Andrew Burgess
2025-09-02 11:03 ` Jan Vraný
0 siblings, 1 reply; 31+ messages in thread
From: Andrew Burgess @ 2025-08-29 14:00 UTC (permalink / raw)
To: Jan Vrany, gdb-patches; +Cc: Jan Vrany, Eli Zaretskii
Jan Vrany <jan.vrany@labware.com> writes:
> 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 15f851e5fd0..7c3a87cdaad 100644
> --- a/gdb/doc/python.texi
> +++ b/gdb/doc/python.texi
> @@ -6822,6 +6822,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 90cba09c88e..5eb40bf3c5b 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 (
GDB style places the opening '(' on the same line as the argument list.
> + 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);
Did you consider updating find_pcs_for_symtab_line to return
'std::vector<linetable_entry *>' ?
find_pcs_for_symtab_line already returns a linetable_entry* in the
best_entry argument, so there's clearly no problem with the callers
accessing that type.
There are only two users of find_pcs_for_symtab_line (one of which is
this one), so updating the function wouldn't be a huge amount of work.
And this function uses a binary search to find the line table entry
we're looking for, which will be better than the linear search you are
using.
> + 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 },
s/NULL/nullptr/ please.
Thanks,
Andrew
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [RFC v5 14/18] gdb/python: add add_symbol () method to gdb.Block
2025-06-23 16:10 ` [RFC v5 14/18] gdb/python: add add_symbol () method to gdb.Block Jan Vrany
@ 2025-08-29 14:10 ` Andrew Burgess
2025-08-29 14:14 ` Andrew Burgess
0 siblings, 1 reply; 31+ messages in thread
From: Andrew Burgess @ 2025-08-29 14:10 UTC (permalink / raw)
To: Jan Vrany, gdb-patches; +Cc: Jan Vrany, Eli Zaretskii
Jan Vrany <jan.vrany@labware.com> writes:
> 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 709ef7c9d1a..15f851e5fd0 100644
> --- a/gdb/doc/python.texi
> +++ b/gdb/doc/python.texi
> @@ -6180,6 +6180,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 43b15b6013e..7926808d4db 100644
> --- a/gdb/python/py-block.c
> +++ b/gdb/python/py-block.c
> @@ -263,6 +263,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"));
> + }
In both these `if` blocks, the '{' and '}' are not needed. In the
second one, is TypeError the best choice? Would ValueError? Or
RuntimeError? be better?
Thanks,
Andrew
> +
> + 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. */
> @@ -360,7 +397,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;
> @@ -400,9 +437,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)
> {
> @@ -417,11 +454,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);
>
> @@ -653,6 +690,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 afa306be00f..b1cb93ca718 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.47.2
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [RFC v5 14/18] gdb/python: add add_symbol () method to gdb.Block
2025-08-29 14:10 ` Andrew Burgess
@ 2025-08-29 14:14 ` Andrew Burgess
0 siblings, 0 replies; 31+ messages in thread
From: Andrew Burgess @ 2025-08-29 14:14 UTC (permalink / raw)
To: Jan Vrany, gdb-patches; +Cc: Jan Vrany, Eli Zaretskii
Andrew Burgess <aburgess@redhat.com> writes:
> Jan Vrany <jan.vrany@labware.com> writes:
>
>> 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 709ef7c9d1a..15f851e5fd0 100644
>> --- a/gdb/doc/python.texi
>> +++ b/gdb/doc/python.texi
>> @@ -6180,6 +6180,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 43b15b6013e..7926808d4db 100644
>> --- a/gdb/python/py-block.c
>> +++ b/gdb/python/py-block.c
>> @@ -263,6 +263,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"));
>> + }
>
> In both these `if` blocks, the '{' and '}' are not needed. In the
> second one, is TypeError the best choice? Would ValueError? Or
> RuntimeError? be better?
>
I just realised I managed to send both these emails to the v5 thread,
rather than the v6 thread. Sorry about that, I was jumping back and
forward looking at different past review comments, and got mixed up at
some point.
I think everything still applies to the v6 patches, if not then just
ignore me :)
The patches I actually downloaded are for sure the v6 ones though!
Sorry about that.
Thanks,
Andrew
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [RFC v5 15/18] gdb/python: add more attributes to gdb.LinetableEntry objects
2025-08-29 14:00 ` Andrew Burgess
@ 2025-09-02 11:03 ` Jan Vraný
0 siblings, 0 replies; 31+ messages in thread
From: Jan Vraný @ 2025-09-02 11:03 UTC (permalink / raw)
To: gdb-patches, aburgess
On Fri, 2025-08-29 at 15:00 +0100, Andrew Burgess wrote:
> Jan Vrany <jan.vrany@labware.com> writes:
...
> > + 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);
>
> Did you consider updating find_pcs_for_symtab_line to return
> 'std::vector<linetable_entry *>' ?
>
> find_pcs_for_symtab_line already returns a linetable_entry* in the
> best_entry argument, so there's clearly no problem with the callers
> accessing that type.
>
> There are only two users of find_pcs_for_symtab_line (one of which is
> this one), so updating the function wouldn't be a huge amount of work.
>
> And this function uses a binary search to find the line table entry
> we're looking for, which will be better than the linear search you are
> using.
I did not actually, but it makes perfect sense. Below patch does that,
I'll include it in the next version.
Thanks!
Jan
-- >8 --
From 0cf3ea738b0f9358660cd8d292b99033a9780e54 Mon Sep 17 00:00:00 2001
From: Jan Vrany <jan.vrany@labware.com>
Date: Mon, 1 Sep 2025 18:18:38 +0100
Subject: [PATCH 1/5] gdb: change find_pcs_for_symtab_line() to return entries
instead of PCs
This commit changes find_pcs_for_symtab_line() to return complete
linetable entries instead of just PCs. This is a preparation for adding
more attributes to gdb.LinetableEntry objects.
I also renamed the function to find_linetable_entries_for_symtab_line()
to better reflect what it does.
---
gdb/linespec.c | 11 ++++++-----
gdb/python/py-linetable.c | 23 ++++++++++++++---------
gdb/symtab.c | 11 +++++------
gdb/symtab.h | 2 +-
4 files changed, 26 insertions(+), 21 deletions(-)
diff --git a/gdb/linespec.c b/gdb/linespec.c
index cefee026d92..9eca52a8e62 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -3984,23 +3984,24 @@ decode_digits_ordinary (struct linespec_state *self,
std::vector<symtab_and_line> sals;
for (const auto &elt : ls->file_symtabs)
{
- std::vector<CORE_ADDR> pcs;
+ std::vector<const linetable_entry *> pcs;
/* The logic above should ensure this. */
gdb_assert (elt != NULL);
- program_space *pspace = elt->compunit ()->objfile ()->pspace ();
+ objfile *objfile = elt->compunit ()->objfile ();
+ program_space *pspace = objfile->pspace ();
set_current_program_space (pspace);
- pcs = find_pcs_for_symtab_line (elt, line, best_entry);
- for (CORE_ADDR pc : pcs)
+ pcs = find_linetable_entries_for_symtab_line (elt, line, best_entry);
+ for (auto linetable_entry : pcs)
{
symtab_and_line sal;
sal.pspace = pspace;
sal.symtab = elt;
sal.line = line;
sal.explicit_line = true;
- sal.pc = pc;
+ sal.pc = linetable_entry->pc (objfile);
sals.push_back (std::move (sal));
}
}
diff --git a/gdb/python/py-linetable.c b/gdb/python/py-linetable.c
index 90cba09c88e..882ae5fe8a8 100644
--- a/gdb/python/py-linetable.c
+++ b/gdb/python/py-linetable.c
@@ -120,22 +120,25 @@ 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)));
if (obj == NULL)
return NULL;
@@ -156,7 +159,7 @@ 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);
@@ -165,14 +168,16 @@ ltpy_get_pcs_for_line (PyObject *self, PyObject *args)
try
{
- pcs = find_pcs_for_symtab_line (symtab, py_line, &best_entry);
+ entries = find_linetable_entries_for_symtab_line (symtab, py_line,
+ &best_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.
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 33475fbbb9e..5c8eb357689 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -3604,13 +3604,12 @@ find_line_symtab (symtab *sym_tab, int line, int *index)
exactly match LINE. Returns an empty vector if there are no exact
matches, but updates BEST_ITEM in this case. */
-std::vector<CORE_ADDR>
-find_pcs_for_symtab_line (struct symtab *symtab, int line,
- const linetable_entry **best_item)
+std::vector<const linetable_entry *>
+find_linetable_entries_for_symtab_line (struct symtab *symtab, int line,
+ const linetable_entry **best_item)
{
int start = 0;
- std::vector<CORE_ADDR> result;
- struct objfile *objfile = symtab->compunit ()->objfile ();
+ std::vector<const linetable_entry *> result;
/* First, collect all the PCs that are at this line. */
while (1)
@@ -3634,7 +3633,7 @@ find_pcs_for_symtab_line (struct symtab *symtab, int line,
break;
}
- result.push_back (symtab->linetable ()->item[idx].pc (objfile));
+ result.push_back (&symtab->linetable ()->item[idx]);
start = idx + 1;
}
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 7ae7451dcfa..91fe763ce43 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -2831,7 +2831,7 @@ bool iterate_over_some_symtabs (const char *name,
void iterate_over_symtabs (program_space *pspace, const char *name,
gdb::function_view<bool (symtab *)> callback);
-std::vector<CORE_ADDR> find_pcs_for_symtab_line
+std::vector<const linetable_entry *> find_linetable_entries_for_symtab_line
(struct symtab *symtab, int line, const linetable_entry **best_entry);
/* Prototype for callbacks for LA_ITERATE_OVER_SYMBOLS. The callback
--
2.50.1
^ permalink raw reply [flat|nested] 31+ messages in thread
end of thread, other threads:[~2025-09-02 11:08 UTC | newest]
Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-06-23 16:09 [RFC v5 00/19] Add Python "JIT" API Jan Vrany
2025-06-23 16:09 ` [RFC v5 01/18] gdb: introduce expand_symtabs_maybe_overlapping Jan Vrany
2025-06-24 15:22 ` Tom Tromey
2025-06-26 15:05 ` Jan Vraný
2025-06-23 16:09 ` [RFC v5 02/18] gdb: introduce compunit_symtab::maybe_contains Jan Vrany
2025-06-23 16:09 ` [RFC v5 03/18] gdb: update is_addr_in_objfile to support "dynamic" objfiles Jan Vrany
2025-06-23 16:09 ` [RFC v5 04/18] gdb: introduce new function create_function_type Jan Vrany
2025-06-24 15:29 ` Tom Tromey
2025-06-26 11:12 ` Jan Vraný
2025-06-27 14:21 ` Tom Tromey
2025-06-27 14:30 ` Jan Vraný
2025-06-23 16:10 ` [RFC v5 05/18] gdb/python: add function () method to gdb.Type object Jan Vrany
2025-06-24 16:11 ` Tom Tromey
2025-06-26 11:13 ` Jan Vraný
2025-06-23 16:10 ` [RFC v5 06/18] gdb: use std::vector<> to hold on blocks in struct blockvector Jan Vrany
2025-06-23 16:10 ` [RFC v5 07/18] gdb/python: add gdb.Compunit Jan Vrany
2025-06-23 16:10 ` [RFC v5 08/18] gdb/python: allow instantiation of gdb.Objfile from Python Jan Vrany
2025-06-23 16:10 ` [RFC v5 09/18] gdb/python: add unlink () method to gdb.Objfile object Jan Vrany
2025-06-23 16:10 ` [RFC v5 10/18] gdb/python: allow instantiation of gdb.Compunit from Python Jan Vrany
2025-06-23 16:10 ` [RFC v5 11/18] gdb/python: allow instantiation of gdb.Symtab " Jan Vrany
2025-06-23 16:10 ` [RFC v5 12/18] gdb/python: allow instantiation of gdb.Block " Jan Vrany
2025-06-23 16:10 ` [RFC v5 13/18] gdb/python: allow instantiation of gdb.Symbol " Jan Vrany
2025-06-23 16:10 ` [RFC v5 14/18] gdb/python: add add_symbol () method to gdb.Block Jan Vrany
2025-08-29 14:10 ` Andrew Burgess
2025-08-29 14:14 ` Andrew Burgess
2025-06-23 16:10 ` [RFC v5 15/18] gdb/python: add more attributes to gdb.LinetableEntry objects Jan Vrany
2025-08-29 14:00 ` Andrew Burgess
2025-09-02 11:03 ` Jan Vraný
2025-06-23 16:10 ` [RFC v5 16/18] gdb/python: allow instantiation of gdb.LineTableEntry objects Jan Vrany
2025-06-23 16:10 ` [RFC v5 17/18] gdb/python: allow instantiation of gdb.LineTable objects Jan Vrany
2025-06-23 16:10 ` [RFC v5 18/18] gdb/python: add section in documentation on implementing JIT interface Jan Vrany
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox