mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-11-16 12:34:43 +00:00
Compare commits
21 Commits
binutils-2
...
users/jv/f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02b804035a | ||
|
|
171a85e261 | ||
|
|
b09d63cdcc | ||
|
|
6b8c17b729 | ||
|
|
917d0f3f6b | ||
|
|
265779df94 | ||
|
|
3150cb09a4 | ||
|
|
2034719b46 | ||
|
|
79c9dc182d | ||
|
|
b0307d2ae4 | ||
|
|
8adaaeb6ad | ||
|
|
840a8c0eee | ||
|
|
42373e5864 | ||
|
|
f44e78463d | ||
|
|
329c759de0 | ||
|
|
e8f5211fe5 | ||
|
|
f350e1aba5 | ||
|
|
418d86ec68 | ||
|
|
6153dea2fb | ||
|
|
d3561c0b69 | ||
|
|
0d2f828428 |
@@ -438,6 +438,7 @@ SUBDIR_PYTHON_SRCS = \
|
||||
python/py-value.c \
|
||||
python/py-varobj.c \
|
||||
python/py-xmethods.c \
|
||||
python/py-compunit.c \
|
||||
python/python.c
|
||||
|
||||
SUBDIR_PYTHON_OBS = $(patsubst %.c,%.o,$(SUBDIR_PYTHON_SRCS))
|
||||
|
||||
17
gdb/NEWS
17
gdb/NEWS
@@ -83,6 +83,23 @@
|
||||
** The 'qualified' argument to gdb.Breakpoint constructor will no
|
||||
longer accept non-bool types.
|
||||
|
||||
** Added gdb.Block.subblocks. Returns a list of blocks contained in that
|
||||
block.
|
||||
|
||||
** Added gdb.Symbol.domain. Contains the domain of the symbol.
|
||||
|
||||
** Added gdb.Architecture.void_type. Returns a gdb.Type representing "void"
|
||||
type for that architecture.
|
||||
|
||||
** 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.
|
||||
|
||||
** 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
|
||||
|
||||
59
gdb/block.c
59
gdb/block.c
@@ -17,6 +17,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <cstring>
|
||||
#include "block.h"
|
||||
#include "symtab.h"
|
||||
#include "symfile.h"
|
||||
@@ -821,6 +822,64 @@ make_blockranges (struct objfile *objfile,
|
||||
return blr;
|
||||
}
|
||||
|
||||
static bool
|
||||
block_ordering_predicate(struct block *b1, struct block *b2)
|
||||
{
|
||||
CORE_ADDR start1 = b1->start ();
|
||||
CORE_ADDR start2 = b2->start ();
|
||||
|
||||
if (start1 != start2)
|
||||
return start1 < start2;
|
||||
return (b2->end () < b1->end ());
|
||||
}
|
||||
|
||||
/* See block.h. */
|
||||
|
||||
void
|
||||
blockvector::add_block (struct block *block)
|
||||
{
|
||||
if (num_blocks() <= FIRST_LOCAL_BLOCK)
|
||||
{
|
||||
/* No blocks (except global and static block). */
|
||||
m_blocks.push_back (block);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* blockvector already contains some blocks. Insert new block
|
||||
to a correct place. */
|
||||
auto first = m_blocks.begin () + FIRST_LOCAL_BLOCK;
|
||||
auto last = m_blocks.end ();
|
||||
|
||||
auto insert_before = std::upper_bound (first,
|
||||
last,
|
||||
block,
|
||||
block_ordering_predicate);
|
||||
|
||||
m_blocks.insert (insert_before, block);
|
||||
}
|
||||
}
|
||||
|
||||
/* See block.h. */
|
||||
|
||||
void
|
||||
blockvector::sort ()
|
||||
{
|
||||
if (num_blocks() > FIRST_LOCAL_BLOCK)
|
||||
{
|
||||
std::sort (blocks ().begin () + FIRST_LOCAL_BLOCK,
|
||||
blocks ().end (),
|
||||
block_ordering_predicate);
|
||||
}
|
||||
}
|
||||
|
||||
/* See block.h. */
|
||||
|
||||
struct blockvector *
|
||||
allocate_blockvector(struct obstack *obstack, int nblocks, int capacity)
|
||||
{
|
||||
return new (obstack) blockvector(obstack, nblocks, capacity);
|
||||
}
|
||||
|
||||
/* Implement 'maint info blocks' command. If passed an argument then
|
||||
print a list of all blocks at the given address. With no arguments
|
||||
then list all blocks at the current address of the current inferior. */
|
||||
|
||||
56
gdb/block.h
56
gdb/block.h
@@ -20,6 +20,7 @@
|
||||
#ifndef BLOCK_H
|
||||
#define BLOCK_H
|
||||
|
||||
#include <algorithm>
|
||||
#include "dictionary.h"
|
||||
#include "gdbsupport/array-view.h"
|
||||
#include "gdbsupport/next-iterator.h"
|
||||
@@ -416,41 +417,60 @@ private:
|
||||
|
||||
struct blockvector
|
||||
{
|
||||
void* operator new (size_t size, struct obstack *obstack)
|
||||
{
|
||||
return obstack_alloc (obstack, size);
|
||||
}
|
||||
|
||||
void* operator new[] (size_t size, struct obstack *obstack)
|
||||
{
|
||||
return obstack_alloc (obstack, size);
|
||||
}
|
||||
|
||||
void operator delete (void *memory) {}
|
||||
void operator delete[] (void *memory) {}
|
||||
|
||||
blockvector (struct obstack *obstack, int nblocks, int capacity = 0)
|
||||
: m_map (nullptr),
|
||||
m_blocks (0, nullptr, obstack_allocator<struct block *> (obstack))
|
||||
{
|
||||
m_blocks.reserve (std::max (nblocks, capacity));
|
||||
m_blocks.resize (nblocks, nullptr);
|
||||
}
|
||||
|
||||
/* Return a view on the blocks of this blockvector. */
|
||||
gdb::array_view<struct block *> blocks ()
|
||||
{
|
||||
return gdb::array_view<struct block *> (m_blocks, m_num_blocks);
|
||||
return gdb::array_view<struct block *> (m_blocks.data (), m_blocks.size ());
|
||||
}
|
||||
|
||||
/* Const version of the above. */
|
||||
gdb::array_view<const struct block *const> blocks () const
|
||||
{
|
||||
const struct block **blocks = (const struct block **) m_blocks;
|
||||
return gdb::array_view<const struct block *const> (blocks, m_num_blocks);
|
||||
const struct block **blocks = (const struct block **) m_blocks.data ();
|
||||
return gdb::array_view<const struct block *const> (blocks, m_blocks.size ());
|
||||
}
|
||||
|
||||
/* Return the block at index I. */
|
||||
struct block *block (size_t i)
|
||||
{ return this->blocks ()[i]; }
|
||||
{ return m_blocks[i]; }
|
||||
|
||||
/* Const version of the above. */
|
||||
const struct block *block (size_t i) const
|
||||
{ return this->blocks ()[i]; }
|
||||
{ return m_blocks[i]; }
|
||||
|
||||
/* Set the block at index I. */
|
||||
void set_block (int i, struct block *block)
|
||||
{ m_blocks[i] = block; }
|
||||
|
||||
/* Set the number of blocks of this blockvector.
|
||||
|
||||
The storage of blocks is done using a flexible array member, so the number
|
||||
of blocks set here must agree with what was effectively allocated. */
|
||||
void set_num_blocks (int num_blocks)
|
||||
{ m_num_blocks = num_blocks; }
|
||||
/* Add BLOCK, making sure blocks are ordered by code-addresses
|
||||
as required. Update global and static block start and end
|
||||
adresses accordingly. */
|
||||
void add_block(struct block *block);
|
||||
|
||||
/* Return the number of blocks in this blockvector. */
|
||||
int num_blocks () const
|
||||
{ return m_num_blocks; }
|
||||
{ return m_blocks.size (); }
|
||||
|
||||
/* Return the global block of this blockvector. */
|
||||
struct global_block *global_block ()
|
||||
@@ -483,19 +503,23 @@ struct blockvector
|
||||
void set_map (addrmap_fixed *map)
|
||||
{ m_map = map; }
|
||||
|
||||
void sort ();
|
||||
|
||||
private:
|
||||
/* An address map mapping addresses to blocks in this blockvector.
|
||||
This pointer is zero if the blocks' start and end addresses are
|
||||
enough. */
|
||||
addrmap_fixed *m_map;
|
||||
|
||||
/* Number of blocks in the list. */
|
||||
int m_num_blocks;
|
||||
|
||||
/* The blocks themselves. */
|
||||
struct block *m_blocks[1];
|
||||
std::vector<struct block *, obstack_allocator<struct block *>> m_blocks;
|
||||
};
|
||||
|
||||
/* Allocate new blockvector for NBLOCKS blocks with enough storage to
|
||||
hold up to CAPACITY blocks. CAPACITY defaults to NBLOCKS. */
|
||||
struct blockvector *allocate_blockvector(struct obstack *obstack,
|
||||
int nblocks, int capacity = 0);
|
||||
|
||||
extern const struct blockvector *blockvector_for_pc (CORE_ADDR,
|
||||
const struct block **);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -39886,7 +39886,8 @@ If you are using @value{GDBN} to debug a program that uses this interface, then
|
||||
it should work transparently so long as you have not stripped the binary. If
|
||||
you are developing a JIT compiler, then the interface is documented in the rest
|
||||
of this chapter. At this time, the only known client of this interface is the
|
||||
LLVM JIT.
|
||||
LLVM JIT. An alternative to interface descrived below is to implement JIT
|
||||
interface in Python (@pxref{JIT Interface in Python}).
|
||||
|
||||
Broadly speaking, the JIT interface mirrors the dynamic loader interface. The
|
||||
JIT compiler communicates with @value{GDBN} by writing data into a global
|
||||
|
||||
@@ -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
|
||||
@@ -232,6 +233,7 @@ optional arguments while skipping others. Example:
|
||||
* Disassembly In Python:: Instruction Disassembly In Python
|
||||
* Missing Debug Info In Python:: Handle missing debug info from Python.
|
||||
* Missing Objfiles In Python:: Handle objfiles from Python.
|
||||
* JIT Interface in Python:: Writing JIT compilation interface in Python
|
||||
@end menu
|
||||
|
||||
@node Basic Python
|
||||
@@ -1574,6 +1576,14 @@ Return a new @code{gdb.Type} object which represents a pointer to this
|
||||
type.
|
||||
@end defun
|
||||
|
||||
@defun Type.function (@r{[}param_type@dots{}@r{]})
|
||||
Return a new @code{gdb.Type} object which represents a type of function
|
||||
returning this type. @code{param_type@dots{}} arguments specify parameter
|
||||
types. Use @code{None} as last parameter type to create a vararg function
|
||||
type. When invoked with single @code{None} argument or with no arguments at
|
||||
all it creates a vararg function taking zero or more parameters.
|
||||
@end defun
|
||||
|
||||
@defun Type.strip_typedefs ()
|
||||
Return a new @code{gdb.Type} that represents the real type,
|
||||
after removing all layers of typedefs.
|
||||
@@ -5647,6 +5657,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
|
||||
@@ -5683,6 +5706,17 @@ Like @code{Objfile.lookup_global_symbol}, but searches for a global
|
||||
symbol with static linkage named @var{name} in this objfile.
|
||||
@end defun
|
||||
|
||||
@defun Objfile.compunits ()
|
||||
Return a sequence of all the compunits associated with this objfile.
|
||||
@xref{Compunits In Python}.
|
||||
@end defun
|
||||
|
||||
@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
|
||||
|
||||
@@ -6004,6 +6038,17 @@ 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.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
|
||||
@@ -6035,11 +6080,21 @@ have a superblock that is not the static block -- for instance this
|
||||
happens for an inlined function.
|
||||
@end defvar
|
||||
|
||||
@defvar Block.compunit
|
||||
The compunit containing this block. @xref{Compunits In Python}.
|
||||
This attribute is not writable.
|
||||
@end defvar
|
||||
|
||||
@defvar Block.superblock
|
||||
The block containing this block. If this parent block does not exist,
|
||||
this attribute holds @code{None}. This attribute is not writable.
|
||||
@end defvar
|
||||
|
||||
@defvar Block.subblocks
|
||||
A list of blocks nested in this block. If there are no blocks nested,
|
||||
this attribute holds an empty list. This attribute is not writable.
|
||||
@end defvar
|
||||
|
||||
@defvar Block.global_block
|
||||
The global block associated with this block. This attribute is not
|
||||
writable.
|
||||
@@ -6198,6 +6253,11 @@ of a symbol. Each address class is a constant defined in the
|
||||
@code{gdb} module and described later in this chapter.
|
||||
@end defvar
|
||||
|
||||
@defvar Symbol.domain
|
||||
The domain of the symbol. Each domain is a constant defined in the
|
||||
@code{gdb} module and described later in this chapter.
|
||||
@end defvar
|
||||
|
||||
@defvar Symbol.needs_frame
|
||||
This is @code{True} if evaluating this symbol's value requires a frame
|
||||
(@pxref{Frames In Python}) and @code{False} otherwise. Typically,
|
||||
@@ -6224,6 +6284,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
|
||||
@@ -6453,8 +6539,17 @@ If no producer information is available then @code{None} is returned.
|
||||
This attribute is not writable.
|
||||
@end defvar
|
||||
|
||||
@defvar Symtab.compunit
|
||||
The compunit this symbol table belongs to. @xref{Compunits In Python}.
|
||||
This attribute is not writable.
|
||||
@end defvar
|
||||
|
||||
A @code{gdb.Symtab} object has the following methods:
|
||||
|
||||
@defun Symtab.__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
|
||||
@@ -6482,6 +6577,75 @@ Return the line table associated with the symbol table.
|
||||
@xref{Line Tables In Python}.
|
||||
@end defun
|
||||
|
||||
@node Compunits In Python
|
||||
@subsubsection Compunits representation in Python
|
||||
|
||||
@cindex compunits in python
|
||||
@tindex gdb.Compunit
|
||||
|
||||
Access to compunits maintained by @value{GDBN} on objfiles
|
||||
is exposed to Python via @code{gdb.Compunit}. Compunit for a symbol table can
|
||||
be accessed via @code{compunit} property of @code{gdb.Symtab} object.
|
||||
@xref{Symbol Tables In Python}. Method @code{compunits} of
|
||||
@code{gdb.Objfile} can be used to get a list of all compunits belonging to
|
||||
that objfile. @xref{Objfiles In Python}.
|
||||
|
||||
A @code{gdb.Compunit} object has the following attributes:
|
||||
|
||||
@defvar Compunit.objfile
|
||||
The compunit's backing object file. @xref{Objfiles In Python}.
|
||||
This attribute is not writable.
|
||||
@end defvar
|
||||
|
||||
@defvar Compunit.producer
|
||||
The name and possibly version number of the program that
|
||||
compiled the code in the compunit.
|
||||
The contents of this string is up to the compiler.
|
||||
If no producer information is available then @code{None} is returned.
|
||||
This attribute is not writable.
|
||||
@end defvar
|
||||
|
||||
@defvar Compunit.symtabs
|
||||
The list of symbol tables associated with this compunit.
|
||||
@xref{Symbol Tables In Python}. This attribute is not writable.
|
||||
@end defvar
|
||||
|
||||
A @code{gdb.Compunit} object has the following methods:
|
||||
|
||||
@defun Compunit.__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
|
||||
the compunit it refers to does not exist in @value{GDBN} any
|
||||
longer. All other @code{gdb.Compunit} methods will throw an exception
|
||||
if it is invalid at the time the method is called.
|
||||
@end defun
|
||||
|
||||
@defun Compunit.global_block ()
|
||||
Return the global block of the underlying compunit.
|
||||
@xref{Blocks In Python}.
|
||||
@end defun
|
||||
|
||||
@defun Compunit.static_block ()
|
||||
Return the static block of the underlying compunit.
|
||||
@xref{Blocks In Python}.
|
||||
@end defun
|
||||
|
||||
@node Line Tables In Python
|
||||
@subsubsection Manipulating line tables using Python
|
||||
|
||||
@@ -6511,6 +6675,31 @@ 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
|
||||
|
||||
@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
|
||||
@@ -6540,6 +6729,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
|
||||
@@ -7080,6 +7277,10 @@ If the indicated type cannot be found, this function will throw a
|
||||
@code{ValueError} exception.
|
||||
@end defun
|
||||
|
||||
@defun Architecture.void_type ()
|
||||
This function returns a void type.
|
||||
@end defun
|
||||
|
||||
@anchor{gdbpy_architecture_registers}
|
||||
@defun Architecture.registers (@r{[} reggroup @r{]})
|
||||
Return a @code{gdb.RegisterDescriptorIterator} (@pxref{Registers In
|
||||
@@ -8354,6 +8555,127 @@ handlers, all of the matching handlers are enabled. The
|
||||
@code{enabled} field of each matching handler is set to @code{True}.
|
||||
@end table
|
||||
|
||||
@node JIT Interface in Python
|
||||
@subsubsection Writing JIT compilation interface in Python
|
||||
@cindex python, just-in-time compilation, JIT compilation interface
|
||||
|
||||
This section provides a high-level overview how to implement a JIT compiler
|
||||
interface entirely in Python. For alternative way of interfacing a JIT
|
||||
@pxref{JIT Interface}.
|
||||
|
||||
A JIT compiler interface usually needs to implement three elements:
|
||||
|
||||
@enumerate
|
||||
@item
|
||||
A way how to get notified when the JIT compiler compiles (and installs) new
|
||||
code and when existing code is discarded. Typical solution is to put a Python
|
||||
breakpoint (@pxref{Breakpoints In Python}) on some function known to be
|
||||
called by the JIT compiler once code is installed or discarded.
|
||||
|
||||
@item
|
||||
When a new code is installed the JIT interface needs to extract (debug)
|
||||
information for newly installed code from the JIT compiler
|
||||
(@pxref{Values From Inferior}) and build @value{GDBN}'s internal structures.
|
||||
@xref{Objfiles In Python}, @ref{Compunits In Python},
|
||||
@ref{Blocks In Python}, @ref{Symbol Tables In Python},
|
||||
@ref{Symbols In Python}, @ref{Line Tables In Python}).
|
||||
|
||||
@item
|
||||
Finally, when (previously installed) code is discarded the JIT interface
|
||||
needs to discard @value{GDBN}'s internal structures built in previous step.
|
||||
This is done by calling @code{unlink} on an objfile for that code
|
||||
(which was created in previous step).
|
||||
@end enumerate
|
||||
|
||||
Here's an example showing how to write a simple JIT interface in Python:
|
||||
|
||||
@c The copy of the code below is also in testsuite/gdb.python/py-jit.py
|
||||
@c and used by py-jit.exp to make sure it is up to date. If changed the
|
||||
@c test and py-jit.py should be checked and update accordingly if needed.
|
||||
@smallexample
|
||||
import gdb
|
||||
|
||||
class JITRegisterCode(gdb.Breakpoint):
|
||||
def stop(self):
|
||||
# Extract new code's address, size, name, linetable (and possibly
|
||||
# other useful information). How exactly to do so depends on JIT
|
||||
# compiler in question.
|
||||
#
|
||||
# In this example address, size and name get passed as parameters
|
||||
# to registration function.
|
||||
|
||||
frame = gdb.newest_frame()
|
||||
addr = int(frame.read_var('code'))
|
||||
size = int(frame.read_var('size'))
|
||||
name = frame.read_var('name').string()
|
||||
linetable_entries = get_linetable_entries(addr)
|
||||
|
||||
# Create objfile and compunit for allocated "jitted" code
|
||||
objfile = gdb.Objfile(name)
|
||||
compunit = gdb.Compunit(name, objfile, addr, addr + size)
|
||||
|
||||
# Mark the objfile as "jitted" code. This will be used later when
|
||||
# unregistering discarded code to check the objfile was indeed
|
||||
# created for "jitted" code.
|
||||
setattr(objfile, "is_jit_code", True)
|
||||
|
||||
# Create block for jitted function
|
||||
block = gdb.Block(compunit.static_block(), addr, addr + size)
|
||||
|
||||
# Create symbol table holding info about jitted function, ...
|
||||
symtab = gdb.Symtab("py-jit.c", compunit)
|
||||
linetable = gdb.LineTable(symtab, linetable_entries)
|
||||
|
||||
# ...type of the jitted function...
|
||||
void_t = gdb.selected_inferior().architecture().void_type()
|
||||
func_t = void_t.function(None)
|
||||
|
||||
# ...and symbol representing jitted function.
|
||||
symbol = gdb.Symbol(name, symtab, func_t,
|
||||
gdb.SYMBOL_FUNCTION_DOMAIN, gdb.SYMBOL_LOC_BLOCK,
|
||||
block)
|
||||
|
||||
# Finally, register the symbol in static block...
|
||||
compunit.static_block().add_symbol(symbol)
|
||||
|
||||
# ..and continue execution
|
||||
return False
|
||||
|
||||
# Create breakpoint to register new code
|
||||
JITRegisterCode("jit_register_code", internal=True)
|
||||
|
||||
|
||||
class JITUnregisterCode(gdb.Breakpoint):
|
||||
def stop(self):
|
||||
# Find out which code has been discarded. Again, how exactly to
|
||||
# do so depends on JIT compiler in question.
|
||||
#
|
||||
# In this example address of discarded code is passed as a
|
||||
# parameter.
|
||||
|
||||
frame = gdb.newest_frame()
|
||||
addr = int(frame.read_var('code'))
|
||||
|
||||
# Find objfile which was created in JITRegisterCode.stop() for
|
||||
# given jitted code.
|
||||
objfile = gdb.current_progspace().objfile_for_address(addr)
|
||||
if objfile is None:
|
||||
# No objfile for given addr (this should not normally happen)
|
||||
return False # Continue execution
|
||||
if not getattr(objfile, "is_jit_code", False):
|
||||
# Not a jitted code (this should not happen either)
|
||||
return False # Continue execution
|
||||
|
||||
# Remove the objfile and all debug info associated with it...
|
||||
objfile.unlink()
|
||||
|
||||
# ..and continue execution
|
||||
return False # Continue execution
|
||||
|
||||
# Create breakpoint to discard old code
|
||||
JITUnregisterCode("jit_unregister_code", internal=True)
|
||||
@end smallexample
|
||||
|
||||
@node Python Auto-loading
|
||||
@subsection Python Auto-loading
|
||||
@cindex Python auto-loading
|
||||
|
||||
@@ -517,7 +517,6 @@ static void
|
||||
finalize_symtab (struct gdb_symtab *stab, struct objfile *objfile)
|
||||
{
|
||||
struct compunit_symtab *cust;
|
||||
size_t blockvector_size;
|
||||
CORE_ADDR begin, end;
|
||||
struct blockvector *bv;
|
||||
|
||||
@@ -552,18 +551,13 @@ finalize_symtab (struct gdb_symtab *stab, struct objfile *objfile)
|
||||
filetab->set_linetable (new_table);
|
||||
}
|
||||
|
||||
blockvector_size = (sizeof (struct blockvector)
|
||||
+ (actual_nblocks - 1) * sizeof (struct block *));
|
||||
bv = (struct blockvector *) obstack_alloc (&objfile->objfile_obstack,
|
||||
blockvector_size);
|
||||
bv = allocate_blockvector(&objfile->objfile_obstack, actual_nblocks);
|
||||
cust->set_blockvector (bv);
|
||||
|
||||
/* At the end of this function, (begin, end) will contain the PC range this
|
||||
entire blockvector spans. */
|
||||
bv->set_map (nullptr);
|
||||
begin = stab->blocks.front ().begin;
|
||||
end = stab->blocks.front ().end;
|
||||
bv->set_num_blocks (actual_nblocks);
|
||||
|
||||
/* First run over all the gdb_block objects, creating a real block
|
||||
object for each. Simultaneously, keep setting the real_block
|
||||
|
||||
@@ -242,8 +242,6 @@ static struct compunit_symtab *new_symtab (const char *, int, struct objfile *);
|
||||
|
||||
static struct linetable *new_linetable (int);
|
||||
|
||||
static struct blockvector *new_bvect (int);
|
||||
|
||||
static struct type *parse_type (int, union aux_ext *, unsigned int, int *,
|
||||
int, const char *);
|
||||
|
||||
@@ -4499,17 +4497,8 @@ add_block (struct block *b, struct symtab *s)
|
||||
/* Cast away "const", but that's ok because we're building the
|
||||
symtab and blockvector here. */
|
||||
struct blockvector *bv
|
||||
= (struct blockvector *) s->compunit ()->blockvector ();
|
||||
|
||||
bv = (struct blockvector *) xrealloc ((void *) bv,
|
||||
(sizeof (struct blockvector)
|
||||
+ bv->num_blocks ()
|
||||
* sizeof (struct block)));
|
||||
if (bv != s->compunit ()->blockvector ())
|
||||
s->compunit ()->set_blockvector (bv);
|
||||
|
||||
bv->set_block (bv->num_blocks (), b);
|
||||
bv->set_num_blocks (bv->num_blocks () + 1);
|
||||
= const_cast<struct blockvector*> (s->compunit ()->blockvector ());
|
||||
bv->add_block (b);
|
||||
}
|
||||
|
||||
/* Add a new linenumber entry (LINENO,ADR) to a linevector LT.
|
||||
@@ -4632,7 +4621,7 @@ new_symtab (const char *name, int maxlines, struct objfile *objfile)
|
||||
lang = cust->language ();
|
||||
|
||||
/* All symtabs must have at least two blocks. */
|
||||
bv = new_bvect (2);
|
||||
bv = allocate_blockvector(&objfile->objfile_obstack, 2);
|
||||
bv->set_block (GLOBAL_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK, lang));
|
||||
bv->set_block (STATIC_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK, lang));
|
||||
bv->static_block ()->set_superblock (bv->global_block ());
|
||||
@@ -4700,21 +4689,6 @@ shrink_linetable (struct linetable *lt)
|
||||
* sizeof (lt->item))));
|
||||
}
|
||||
|
||||
/* Allocate and zero a new blockvector of NBLOCKS blocks. */
|
||||
|
||||
static struct blockvector *
|
||||
new_bvect (int nblocks)
|
||||
{
|
||||
struct blockvector *bv;
|
||||
int size;
|
||||
|
||||
size = sizeof (struct blockvector) + nblocks * sizeof (struct block *);
|
||||
bv = (struct blockvector *) xzalloc (size);
|
||||
bv->set_num_blocks (nblocks);
|
||||
|
||||
return bv;
|
||||
}
|
||||
|
||||
/* Allocate and zero a new block of language LANGUAGE, and set its
|
||||
BLOCK_MULTIDICT. If function is non-zero, assume the block is
|
||||
associated to a function, and make sure that the symbols are stored
|
||||
|
||||
@@ -1194,6 +1194,22 @@ is_addr_in_objfile (CORE_ADDR addr, const struct objfile *objfile)
|
||||
if (osect->contains (addr))
|
||||
return true;
|
||||
}
|
||||
/* Objfiles created dynamically by JIT reader API (and possibly by
|
||||
other means too) do not have sections and therefore the above
|
||||
check never succeeds.
|
||||
|
||||
For such "dynamic" objfiles walk over all compunits and check
|
||||
if given ADDR falls into compunit's global block. */
|
||||
if (! objfile->sections_start)
|
||||
{
|
||||
for (compunit_symtab *cu
|
||||
: const_cast<struct objfile *>(objfile)->compunits ())
|
||||
{
|
||||
global_block *gb = cu->blockvector ()->global_block ();
|
||||
if (gb->start () <= addr && addr <= gb->end ())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1296,3 +1312,23 @@ objfile_int_type (struct objfile *of, int size_in_bytes, bool unsigned_p)
|
||||
|
||||
gdb_assert_not_reached ("unable to find suitable integer type");
|
||||
}
|
||||
|
||||
/* See objfiles.h. */
|
||||
|
||||
int
|
||||
objfile::find_section_index (CORE_ADDR start, CORE_ADDR end)
|
||||
{
|
||||
obj_section *sect;
|
||||
int sect_index;
|
||||
for (sect = this->sections_start, sect_index = 0;
|
||||
sect < this->sections_end;
|
||||
sect++, sect_index++)
|
||||
{
|
||||
if (sect->the_bfd_section == nullptr)
|
||||
continue;
|
||||
|
||||
if (sect->addr () <= start && end <= sect->endaddr ())
|
||||
return sect_index;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -644,6 +644,10 @@ public:
|
||||
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:
|
||||
|
||||
@@ -318,6 +318,18 @@ archpy_integer_type (PyObject *self, PyObject *args, PyObject *kw)
|
||||
return type_to_type_object (type);
|
||||
}
|
||||
|
||||
/* Implementation of gdb.void_type. */
|
||||
static PyObject *
|
||||
archpy_void_type (PyObject *self, PyObject *args)
|
||||
{
|
||||
struct gdbarch *gdbarch;
|
||||
ARCHPY_REQUIRE_VALID (self, gdbarch);
|
||||
|
||||
builtin_type (gdbarch);
|
||||
|
||||
return type_to_type_object (builtin_type (gdbarch)->builtin_void);
|
||||
}
|
||||
|
||||
/* __repr__ implementation for gdb.Architecture. */
|
||||
|
||||
static PyObject *
|
||||
@@ -383,6 +395,10 @@ END_PC." },
|
||||
"integer_type (size [, signed]) -> type\n\
|
||||
Return an integer Type corresponding to the given bitsize and signed-ness.\n\
|
||||
If not specified, the type defaults to signed." },
|
||||
{ "void_type", (PyCFunction) archpy_void_type,
|
||||
METH_NOARGS,
|
||||
"void_type () -> type\n\
|
||||
Return an void Type." },
|
||||
{ "registers", (PyCFunction) archpy_registers,
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
"registers ([ group-name ]) -> Iterator.\n\
|
||||
|
||||
@@ -149,6 +149,34 @@ blpy_get_superblock (PyObject *self, void *closure)
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
blpy_get_subblocks (PyObject *self, void *closure)
|
||||
{
|
||||
const struct block *block;
|
||||
|
||||
BLPY_REQUIRE_VALID (self, block);
|
||||
|
||||
gdbpy_ref<> list (PyList_New (0));
|
||||
if (list == nullptr)
|
||||
return nullptr;
|
||||
|
||||
compunit_symtab *cu = block->global_block ()->compunit ();
|
||||
|
||||
for (const struct block *each : cu->blockvector ()->blocks ())
|
||||
{
|
||||
if (each->superblock () == block)
|
||||
{
|
||||
gdbpy_ref<> item (block_to_block_object (each, cu->objfile ()));
|
||||
|
||||
if (item.get () == nullptr
|
||||
|| PyList_Append (list.get (), item.get ()) == -1)
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return list.release ();
|
||||
}
|
||||
|
||||
/* Return the global block associated to this block. */
|
||||
|
||||
static PyObject *
|
||||
@@ -187,6 +215,19 @@ blpy_get_static_block (PyObject *self, void *closure)
|
||||
return block_to_block_object (static_block, self_obj->objfile);
|
||||
}
|
||||
|
||||
/* Getter function for Block.compunit. */
|
||||
|
||||
static PyObject *
|
||||
blpy_get_compunit (PyObject *self, void *closure)
|
||||
{
|
||||
const struct block *block;
|
||||
|
||||
BLPY_REQUIRE_VALID (self, block);
|
||||
|
||||
return compunit_to_compunit_object (
|
||||
block->global_block ()->compunit ()).release ();
|
||||
}
|
||||
|
||||
/* Implementation of gdb.Block.is_global (self) -> Boolean.
|
||||
Returns True if this block object is a global block. */
|
||||
|
||||
@@ -220,6 +261,43 @@ blpy_is_static (PyObject *self, void *closure)
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
|
||||
/* Implementation of gdb.Block.add_symbol (self, symbol).
|
||||
Adds SYMBOL to this block. */
|
||||
|
||||
static PyObject *
|
||||
blpy_add_symbol (PyObject *self, PyObject *symbol_obj)
|
||||
{
|
||||
const struct block *block;
|
||||
|
||||
BLPY_REQUIRE_VALID (self, block);
|
||||
|
||||
struct symbol *symbol = symbol_object_to_symbol (symbol_obj);
|
||||
if (symbol == nullptr)
|
||||
{
|
||||
return PyErr_Format (PyExc_TypeError,
|
||||
_("The symbol argument is not valid gdb.Symbol"));
|
||||
}
|
||||
|
||||
if (symbol->symtab ()->compunit() != block->global_block ()->compunit ())
|
||||
{
|
||||
return PyErr_Format (PyExc_TypeError,
|
||||
_("The symbol argument belongs to different "
|
||||
"compunit than block"));
|
||||
}
|
||||
|
||||
multidictionary *dict = block->multidict ();
|
||||
if (dict == nullptr)
|
||||
{
|
||||
auto_obstack *obstack =
|
||||
&(block->global_block ()->compunit ()->objfile ()->objfile_obstack);
|
||||
dict = mdict_create_linear (obstack, nullptr);
|
||||
const_cast<struct block *>(block)->set_multidict (dict);
|
||||
}
|
||||
|
||||
mdict_add_symbol (dict, symbol);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/* Given a string, returns the gdb.Symbol representing that symbol in this
|
||||
block. If such a symbol does not exist, returns NULL with a Python
|
||||
exception. */
|
||||
@@ -301,6 +379,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", nullptr };
|
||||
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. */
|
||||
compunit_symtab *cu = superblock->global_block ()->compunit ();
|
||||
|
||||
for (const struct block *each : cu->blockvector ()->blocks ())
|
||||
{
|
||||
if (each->superblock () == superblock)
|
||||
{
|
||||
/* each is a "sibling" block. */
|
||||
if (std::max (start, each->start ()) < std::min(end, each->end ()))
|
||||
{
|
||||
PyErr_Format (PyExc_ValueError,
|
||||
_("The start-end range overlaps with one of the "
|
||||
"sibling blocks"));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto_obstack *obstack = &(cu->objfile ()->objfile_obstack);
|
||||
struct block *blk = new (obstack) block ();
|
||||
|
||||
blk->set_superblock (superblock);
|
||||
blk->set_multidict (mdict_create_linear (obstack, nullptr));
|
||||
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 *
|
||||
@@ -492,7 +670,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;
|
||||
|
||||
@@ -511,6 +688,9 @@ static PyMethodDef block_object_methods[] = {
|
||||
{ "is_valid", blpy_is_valid, METH_NOARGS,
|
||||
"is_valid () -> Boolean.\n\
|
||||
Return true if this block is valid, false if not." },
|
||||
{ "add_symbol", blpy_add_symbol, METH_O,
|
||||
"add_symbol (symbol) -> None.\n\
|
||||
Add given symbol to the block." },
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
@@ -525,10 +705,14 @@ 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,
|
||||
"Whether this block is a global block.", NULL },
|
||||
{ "subblocks", blpy_get_subblocks, nullptr,
|
||||
"List of blocks contained in this block.", nullptr },
|
||||
{ NULL } /* Sentinel */
|
||||
};
|
||||
|
||||
@@ -568,7 +752,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[] = {
|
||||
|
||||
414
gdb/python/py-compunit.c
Normal file
414
gdb/python/py-compunit.c
Normal file
@@ -0,0 +1,414 @@
|
||||
/* Python interface to compunits.
|
||||
|
||||
Copyright (C) 2008-2024 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GDB.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <algorithm>
|
||||
#include "charset.h"
|
||||
#include "symtab.h"
|
||||
#include "source.h"
|
||||
#include "python-internal.h"
|
||||
#include "objfiles.h"
|
||||
#include "block.h"
|
||||
|
||||
struct compunit_object {
|
||||
PyObject_HEAD
|
||||
|
||||
/* The GDB compunit structure. */
|
||||
struct compunit_symtab *compunit;
|
||||
|
||||
/* A compunit object is associated with an objfile, so keep track with
|
||||
a doubly-linked list, rooted in the objfile. This allows
|
||||
invalidation of the underlying struct compunit_symtab when the objfile is
|
||||
deleted. */
|
||||
compunit_object *prev;
|
||||
compunit_object *next;
|
||||
};
|
||||
|
||||
/* This function is called when an objfile is about to be freed.
|
||||
Invalidate the compunit as further actions on the compunit
|
||||
would result in bad data. All access to obj->compunit should be
|
||||
gated by CUPY_REQUIRE_VALID which will raise an exception on
|
||||
compunits. */
|
||||
struct cupy_deleter
|
||||
{
|
||||
void operator() (compunit_object *obj)
|
||||
{
|
||||
while (obj)
|
||||
{
|
||||
compunit_object *next = obj->next;
|
||||
|
||||
obj->compunit = nullptr;
|
||||
obj->next = nullptr;
|
||||
obj->prev = nullptr;
|
||||
obj = next;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
extern PyTypeObject compunit_object_type
|
||||
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("compunit_object");
|
||||
static const registry<objfile>::key<compunit_object, cupy_deleter>
|
||||
cupy_objfile_data_key;
|
||||
|
||||
/* Require a valid compunit. All access to compunit_object->compunit
|
||||
should be gated by this call. */
|
||||
|
||||
#define CUPY_REQUIRE_VALID(compunit_obj, compunit) \
|
||||
do { \
|
||||
compunit = compunit_object_to_compunit (compunit_obj); \
|
||||
if (compunit == nullptr) \
|
||||
{ \
|
||||
PyErr_SetString (PyExc_RuntimeError, \
|
||||
_("Compunit object is invalid.")); \
|
||||
return nullptr; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* Getter function for gdb.Compunit.objfile. */
|
||||
|
||||
static PyObject *
|
||||
cupy_get_objfile (PyObject *self, void *closure)
|
||||
{
|
||||
struct compunit_symtab *compunit = nullptr;
|
||||
|
||||
CUPY_REQUIRE_VALID (self, compunit);
|
||||
|
||||
return objfile_to_objfile_object (compunit->objfile ()).release ();
|
||||
}
|
||||
|
||||
/* Getter function for gdb.Compunit.producer. */
|
||||
|
||||
static PyObject *
|
||||
cupy_get_producer (PyObject *self, void *closure)
|
||||
{
|
||||
struct compunit_symtab *compunit = nullptr;
|
||||
|
||||
CUPY_REQUIRE_VALID (self, compunit);
|
||||
if (compunit->producer () != nullptr)
|
||||
{
|
||||
const char *producer = compunit->producer ();
|
||||
|
||||
return host_string_to_python_string (producer).release ();
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/* Implementation of gdb.Compunit.is_valid (self) -> Boolean.
|
||||
Returns True if this Symbol table still exists in GDB. */
|
||||
|
||||
static PyObject *
|
||||
cupy_is_valid (PyObject *self, PyObject *args)
|
||||
{
|
||||
struct compunit_symtab *compunit = nullptr;
|
||||
|
||||
compunit = compunit_object_to_compunit (self);
|
||||
if (compunit == nullptr)
|
||||
Py_RETURN_FALSE;
|
||||
|
||||
Py_RETURN_TRUE;
|
||||
}
|
||||
|
||||
/* Return the GLOBAL_BLOCK of the underlying compunit. */
|
||||
|
||||
static PyObject *
|
||||
cupy_global_block (PyObject *self, PyObject *args)
|
||||
{
|
||||
struct compunit_symtab *compunit = nullptr;
|
||||
const struct blockvector *blockvector;
|
||||
|
||||
CUPY_REQUIRE_VALID (self, compunit);
|
||||
|
||||
blockvector = compunit->blockvector ();
|
||||
const struct block *block = blockvector->global_block ();
|
||||
|
||||
return block_to_block_object (block, compunit->objfile ());
|
||||
}
|
||||
|
||||
/* Return the STATIC_BLOCK of the underlying compunit. */
|
||||
|
||||
static PyObject *
|
||||
cupy_static_block (PyObject *self, PyObject *args)
|
||||
{
|
||||
struct compunit_symtab *compunit = nullptr;
|
||||
const struct blockvector *blockvector;
|
||||
|
||||
CUPY_REQUIRE_VALID (self, compunit);
|
||||
|
||||
blockvector = compunit->blockvector ();
|
||||
const struct block *block = blockvector->static_block ();
|
||||
|
||||
return block_to_block_object (block, compunit->objfile ());
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
cupy_get_symtabs (PyObject *self, void *closure)
|
||||
{
|
||||
struct compunit_symtab *compunit = nullptr;
|
||||
|
||||
CUPY_REQUIRE_VALID (self, compunit);
|
||||
|
||||
gdbpy_ref<> list (PyList_New (0));
|
||||
if (list == nullptr)
|
||||
return nullptr;
|
||||
|
||||
for (struct symtab *each : compunit->filetabs ())
|
||||
{
|
||||
{
|
||||
gdbpy_ref<> item (symtab_to_symtab_object (each));
|
||||
if (item.get () == nullptr
|
||||
|| PyList_Append (list.get (), item.get ()) == -1)
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return list.release ();
|
||||
}
|
||||
|
||||
static void
|
||||
cupy_dealloc (PyObject *obj)
|
||||
{
|
||||
compunit_object *compunit = (compunit_object *) obj;
|
||||
|
||||
if (compunit->prev)
|
||||
compunit->prev->next = compunit->next;
|
||||
else if (compunit->compunit)
|
||||
cupy_objfile_data_key.set (compunit->compunit->objfile (),
|
||||
compunit->next);
|
||||
if (compunit->next)
|
||||
compunit->next->prev = compunit->prev;
|
||||
compunit->compunit = nullptr;
|
||||
Py_TYPE (obj)->tp_free (obj);
|
||||
}
|
||||
|
||||
/* Given a compunit, and a compunit_object that has previously been
|
||||
allocated and initialized, populate the compunit_object with the
|
||||
struct compunit_symtab data. Also, register the compunit_object life-cycle
|
||||
with the life-cycle of the object file associated with this
|
||||
compunit, if needed. */
|
||||
static void
|
||||
set_compunit (compunit_object *obj, struct compunit_symtab *compunit)
|
||||
{
|
||||
obj->compunit = compunit;
|
||||
obj->prev = nullptr;
|
||||
if (compunit)
|
||||
{
|
||||
obj->next = cupy_objfile_data_key.get (compunit->objfile ());
|
||||
if (obj->next)
|
||||
obj->next->prev = obj;
|
||||
cupy_objfile_data_key.set (compunit->objfile (), obj);
|
||||
}
|
||||
else
|
||||
obj->next = nullptr;
|
||||
}
|
||||
|
||||
/* Object initializer; creates a new compunit.
|
||||
|
||||
Use: __init__(FILENAME, OBJFILE, START, END [, CAPACITY]). */
|
||||
|
||||
static int
|
||||
cupy_init (PyObject *zelf, PyObject *args, PyObject *kw)
|
||||
{
|
||||
struct compunit_object *self = (struct compunit_object*) zelf;
|
||||
|
||||
if (self->compunit)
|
||||
{
|
||||
PyErr_Format (PyExc_RuntimeError,
|
||||
_("Compunit object already initialized."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const char *keywords[] = { "filename", "objfile", "start", "end",
|
||||
"capacity", nullptr };
|
||||
const char *filename;
|
||||
PyObject *objf_obj = nullptr;
|
||||
uint64_t start = 0;
|
||||
uint64_t end = 0;
|
||||
uint64_t capacity = 8;
|
||||
|
||||
|
||||
if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "sOKK|K", keywords,
|
||||
&filename, &objf_obj, &start, &end,
|
||||
&capacity))
|
||||
return -1;
|
||||
|
||||
auto objf = objfile_object_to_objfile (objf_obj);
|
||||
if (! objf)
|
||||
{
|
||||
PyErr_Format (PyExc_TypeError,
|
||||
_("The objfile argument is not valid gdb.Objfile object"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check that start-end range is valid. */
|
||||
if (! (start <= end))
|
||||
{
|
||||
PyErr_Format (PyExc_ValueError,
|
||||
_("The start argument must be less or equal to the end "
|
||||
"argument"));
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
/* Check that to-be created compunits' global block does not overlap
|
||||
with existing compunit. */
|
||||
|
||||
for (struct objfile *of : objf->pspace ()->objfiles_safe ())
|
||||
{
|
||||
for (compunit_symtab *cu : of->compunits ())
|
||||
{
|
||||
global_block *gb = cu->blockvector ()->global_block ();
|
||||
if (std::max (start, gb->start ()) < std::min(end, gb->end ()))
|
||||
{
|
||||
PyErr_Format (PyExc_ValueError,
|
||||
_("The start-end range overlaps with existing compunit"));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blockvector *bv = allocate_blockvector (&objf->objfile_obstack,
|
||||
FIRST_LOCAL_BLOCK, capacity);
|
||||
compunit_symtab *cu = allocate_compunit_symtab (objf, filename);
|
||||
cu->set_dirname (nullptr);
|
||||
cu->set_blockvector (bv);
|
||||
|
||||
/* Allocate global block. */
|
||||
global_block *gb = new (&objf->objfile_obstack) global_block ();
|
||||
gb->set_multidict (mdict_create_linear_expandable (language_minimal));
|
||||
gb->set_start ((CORE_ADDR) start);
|
||||
gb->set_end ((CORE_ADDR) end);
|
||||
gb->set_compunit (cu);
|
||||
bv->set_block (GLOBAL_BLOCK, gb);
|
||||
|
||||
/* Allocate static block. */
|
||||
struct block *sb = new (&objf->objfile_obstack) block ();
|
||||
sb->set_multidict (mdict_create_linear_expandable (language_minimal));
|
||||
sb->set_start ((CORE_ADDR) start);
|
||||
sb->set_end ((CORE_ADDR) end);
|
||||
sb->set_superblock (gb);
|
||||
bv->set_block (STATIC_BLOCK, sb);
|
||||
|
||||
add_compunit_symtab_to_objfile (cu);
|
||||
|
||||
set_compunit(self, cu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return a new reference to gdb.Compunit Python object representing
|
||||
COMPUNIT. Return NULL and set the Python error on failure. */
|
||||
gdbpy_ref<>
|
||||
compunit_to_compunit_object (struct compunit_symtab *compunit)
|
||||
{
|
||||
compunit_object *compunit_obj;
|
||||
|
||||
compunit_obj = PyObject_NEW(compunit_object, &compunit_object_type);
|
||||
if (compunit_obj)
|
||||
set_compunit(compunit_obj, compunit);
|
||||
|
||||
return gdbpy_ref<>::new_reference ((PyObject * )compunit_obj);
|
||||
}
|
||||
|
||||
/* Return struct compunit_symtab reference that is wrapped by this object. */
|
||||
struct compunit_symtab *
|
||||
compunit_object_to_compunit (PyObject *obj)
|
||||
{
|
||||
if (! PyObject_TypeCheck (obj, &compunit_object_type))
|
||||
return nullptr;
|
||||
return ((compunit_object *) obj)->compunit;
|
||||
}
|
||||
|
||||
static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
|
||||
gdbpy_initialize_compunits (void)
|
||||
{
|
||||
if (gdbpy_type_ready (&compunit_object_type) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
GDBPY_INITIALIZE_FILE (gdbpy_initialize_compunits);
|
||||
|
||||
|
||||
|
||||
static gdb_PyGetSetDef compunit_object_getset[] = {
|
||||
{ "objfile", cupy_get_objfile, nullptr, "The compunit's objfile.",
|
||||
nullptr },
|
||||
{ "producer", cupy_get_producer, nullptr,
|
||||
"The name/version of the program that compiled this compunit.", nullptr },
|
||||
{ "symtabs", cupy_get_symtabs, nullptr,
|
||||
"List of symbol tables associated with this compunit", nullptr },
|
||||
{nullptr} /* Sentinel */
|
||||
};
|
||||
|
||||
static PyMethodDef compunit_object_methods[] = {
|
||||
{ "is_valid", cupy_is_valid, METH_NOARGS,
|
||||
"is_valid () -> Boolean.\n\
|
||||
Return true if this compunit is valid, false if not." },
|
||||
{ "global_block", cupy_global_block, METH_NOARGS,
|
||||
"global_block () -> gdb.Block.\n\
|
||||
Return the global block of the compunit." },
|
||||
{ "static_block", cupy_static_block, METH_NOARGS,
|
||||
"static_block () -> gdb.Block.\n\
|
||||
Return the static block of the compunit." },
|
||||
{nullptr} /* Sentinel */
|
||||
};
|
||||
|
||||
PyTypeObject compunit_object_type = {
|
||||
PyVarObject_HEAD_INIT (nullptr, 0)
|
||||
"gdb.Compunit", /*tp_name*/
|
||||
sizeof (compunit_object), /*tp_basicsize*/
|
||||
0, /*tp_itemsize*/
|
||||
cupy_dealloc, /*tp_dealloc*/
|
||||
0, /*tp_print*/
|
||||
0, /*tp_getattr*/
|
||||
0, /*tp_setattr*/
|
||||
0, /*tp_compare*/
|
||||
0, /*tp_repr*/
|
||||
0, /*tp_as_number*/
|
||||
0, /*tp_as_sequence*/
|
||||
0, /*tp_as_mapping*/
|
||||
0, /*tp_hash */
|
||||
0, /*tp_call*/
|
||||
0, /*tp_str*/
|
||||
0, /*tp_getattro*/
|
||||
0, /*tp_setattro*/
|
||||
0, /*tp_as_buffer*/
|
||||
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
||||
"GDB compunit object", /*tp_doc */
|
||||
0, /*tp_traverse */
|
||||
0, /*tp_clear */
|
||||
gdbpy_richcompare<compunit_object, compunit_symtab,
|
||||
&compunit_object::compunit>,/*tp_richcompare */
|
||||
0, /*tp_weaklistoffset */
|
||||
0, /*tp_iter */
|
||||
0, /*tp_iternext */
|
||||
compunit_object_methods, /*tp_methods */
|
||||
0, /*tp_members */
|
||||
compunit_object_getset, /*tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
cupy_init, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
PyType_GenericNew /* tp_new */
|
||||
};
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -25,6 +27,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 +106,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 +117,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 +132,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));
|
||||
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));
|
||||
|
||||
if (obj == NULL)
|
||||
return NULL;
|
||||
@@ -155,24 +171,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.
|
||||
@@ -269,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. */
|
||||
|
||||
@@ -277,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);
|
||||
}
|
||||
|
||||
@@ -321,6 +445,85 @@ 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;
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
@@ -406,7 +609,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;
|
||||
@@ -486,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[] = {
|
||||
@@ -534,9 +739,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, 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 */
|
||||
};
|
||||
|
||||
|
||||
PyTypeObject linetable_entry_object_type = {
|
||||
PyVarObject_HEAD_INIT (NULL, 0)
|
||||
"gdb.LineTableEntry", /*tp_name*/
|
||||
@@ -573,6 +785,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 */
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
@@ -529,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 *
|
||||
@@ -544,6 +637,31 @@ objfpy_repr (PyObject *self_)
|
||||
objfile_name (obj));
|
||||
}
|
||||
|
||||
/* Implementation of gdb.Objfile.compunits() -> List */
|
||||
|
||||
static PyObject *
|
||||
objfpy_compunits (PyObject *self_, PyObject *args)
|
||||
{
|
||||
objfile_object *self = (objfile_object *) self_;
|
||||
|
||||
OBJFPY_REQUIRE_VALID (self);
|
||||
|
||||
gdbpy_ref<> list (PyList_New (0));
|
||||
if (list == nullptr)
|
||||
return nullptr;
|
||||
|
||||
for (struct compunit_symtab *compunit : self->objfile->compunits ())
|
||||
{
|
||||
gdbpy_ref<> item = compunit_to_compunit_object (compunit);
|
||||
|
||||
if (item.get () == nullptr
|
||||
|| PyList_Append (list.get (), item.get ()) == -1)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return list.release ();
|
||||
}
|
||||
|
||||
/* Subroutine of gdbpy_lookup_objfile_by_build_id to simplify it.
|
||||
Return non-zero if STRING is a potentially valid build id. */
|
||||
|
||||
@@ -706,6 +824,44 @@ 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. */
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -737,6 +893,14 @@ 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." },
|
||||
|
||||
{ "unlink", objfpy_unlink, METH_NOARGS,
|
||||
"unlink ().\n\
|
||||
Remove this objfile." },
|
||||
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
@@ -806,8 +970,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 */
|
||||
};
|
||||
|
||||
@@ -153,6 +153,16 @@ sympy_get_addr_class (PyObject *self, void *closure)
|
||||
return gdb_py_object_from_longest (symbol->aclass ()).release ();
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
sympy_get_domain (PyObject *self, void *closure)
|
||||
{
|
||||
struct symbol *symbol = nullptr;
|
||||
|
||||
SYMPY_REQUIRE_VALID (self, symbol);
|
||||
|
||||
return gdb_py_object_from_longest (symbol->domain ()).release ();
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
sympy_is_argument (PyObject *self, void *closure)
|
||||
{
|
||||
@@ -390,6 +400,135 @@ sympy_repr (PyObject *self)
|
||||
symbol->print_name ());
|
||||
}
|
||||
|
||||
/* Object initializer; creates new symbol.
|
||||
|
||||
Use: __init__(NAME, SYMTAB, TYPE, DOMAIN, ADDR_CLASS, VALUE). */
|
||||
|
||||
static int
|
||||
sympy_init (PyObject *zelf, PyObject *args, PyObject *kw)
|
||||
{
|
||||
struct symbol_object *self = (struct symbol_object*) zelf;
|
||||
|
||||
if (self->symbol)
|
||||
{
|
||||
PyErr_Format (PyExc_RuntimeError,
|
||||
_("Symbol object already initialized."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const char *keywords[] = { "name", "symtab", "type",
|
||||
"domain", "addr_class", "value",
|
||||
nullptr };
|
||||
const char *name;
|
||||
PyObject *symtab_obj = nullptr;
|
||||
PyObject *type_obj = nullptr;
|
||||
domain_enum domain;
|
||||
unsigned int addr_class;
|
||||
PyObject *value_obj = nullptr;
|
||||
|
||||
if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "sOOIIO", keywords,
|
||||
&name, &symtab_obj, &type_obj,
|
||||
&domain, &addr_class, &value_obj))
|
||||
return -1;
|
||||
|
||||
|
||||
struct symtab *symtab = symtab_object_to_symtab (symtab_obj);
|
||||
if (symtab == nullptr)
|
||||
{
|
||||
PyErr_Format (PyExc_TypeError,
|
||||
_("The symtab argument is not valid gdb.Symtab object"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct type *type = type_object_to_type (type_obj);
|
||||
if (type == nullptr)
|
||||
{
|
||||
PyErr_Format (PyExc_TypeError,
|
||||
_("The type argument is not valid gdb.Type object"));
|
||||
return -1;
|
||||
}
|
||||
if (type->objfile_owner () != nullptr &&
|
||||
type->objfile_owner () != symtab->compunit ()->objfile ())
|
||||
{
|
||||
PyErr_Format (PyExc_ValueError,
|
||||
_("The type argument's owning objfile differs from "
|
||||
"symtab's objfile."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
union _value {
|
||||
const struct block *block;
|
||||
} value;
|
||||
|
||||
switch (addr_class)
|
||||
{
|
||||
default:
|
||||
PyErr_Format (PyExc_ValueError,
|
||||
_("The value of addr_class argument is not supported"));
|
||||
return -1;
|
||||
|
||||
case LOC_BLOCK:
|
||||
if ((value.block = block_object_to_block (value_obj)) == nullptr)
|
||||
{
|
||||
PyErr_Format (PyExc_TypeError,
|
||||
_("The addr_class argument is SYMBOL_LOC_BLOCK but "
|
||||
"the value argument is not a valid gdb.Block."));
|
||||
return -1;
|
||||
}
|
||||
if (type->code () != TYPE_CODE_FUNC)
|
||||
{
|
||||
PyErr_Format (PyExc_ValueError,
|
||||
_("The addr_class argument is SYMBOL_LOC_BLOCK but "
|
||||
"the type argument is not a function type."));
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
struct objfile *objfile = symtab->compunit ()->objfile ();
|
||||
auto_obstack *obstack = &(objfile->objfile_obstack);
|
||||
struct symbol *sym = new (obstack) symbol();
|
||||
|
||||
sym->m_name = obstack_strdup (obstack, name);
|
||||
sym->set_symtab (symtab);
|
||||
sym->set_type (type);
|
||||
sym->set_domain (domain);
|
||||
sym->set_aclass_index (addr_class);
|
||||
|
||||
switch (addr_class)
|
||||
{
|
||||
case LOC_BLOCK:
|
||||
{
|
||||
sym->set_value_block (value.block);
|
||||
|
||||
if (domain == FUNCTION_DOMAIN)
|
||||
const_cast<struct block*> (value.block)->set_function (sym);
|
||||
|
||||
/* Set symbol's section index. This needed in somewhat unusual
|
||||
usecase where dynamic code is generated into a special section
|
||||
(defined in custom linker script or otherwise). Otherwise,
|
||||
find_pc_sect_compunit_symtab () would not find the compunit
|
||||
symtab and commands like "disassemble function_name" would
|
||||
resort to disassemble complete section.
|
||||
|
||||
Note that in usual case where new objfile is created for
|
||||
dynamic code, the objfile has no sections at all and
|
||||
objfile::find_section_index () returns -1.
|
||||
*/
|
||||
CORE_ADDR start = value.block->start ();
|
||||
CORE_ADDR end = value.block->end ();
|
||||
sym->set_section_index (objfile->find_section_index (start, end));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
gdb_assert_not_reached("unreachable");
|
||||
break;
|
||||
}
|
||||
|
||||
set_symbol (self, sym);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Implementation of
|
||||
gdb.lookup_symbol (name [, block] [, domain]) -> (symbol, is_field_of_this)
|
||||
A tuple with 2 elements is always returned. The first is the symbol
|
||||
@@ -707,6 +846,7 @@ static gdb_PyGetSetDef symbol_object_getset[] = {
|
||||
This is either name or linkage_name, depending on whether the user asked GDB\n\
|
||||
to display demangled or mangled names.", NULL },
|
||||
{ "addr_class", sympy_get_addr_class, NULL, "Address class of the symbol." },
|
||||
{ "domain", sympy_get_domain, nullptr, "Domain of the symbol." },
|
||||
{ "is_argument", sympy_is_argument, NULL,
|
||||
"True if the symbol is an argument of a function." },
|
||||
{ "is_constant", sympy_is_constant, NULL,
|
||||
@@ -756,11 +896,20 @@ PyTypeObject symbol_object_type = {
|
||||
"GDB symbol object", /*tp_doc */
|
||||
0, /*tp_traverse */
|
||||
0, /*tp_clear */
|
||||
0, /*tp_richcompare */
|
||||
gdbpy_richcompare<symbol_object, symbol, &symbol_object::symbol>,
|
||||
/*tp_richcompare */
|
||||
0, /*tp_weaklistoffset */
|
||||
0, /*tp_iter */
|
||||
0, /*tp_iternext */
|
||||
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 */
|
||||
};
|
||||
|
||||
@@ -136,6 +136,8 @@ static const registry<objfile>::key<sal_object, salpy_deleter>
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static void set_symtab (symtab_object *obj, struct symtab *symtab);
|
||||
|
||||
static PyObject *
|
||||
stpy_str (PyObject *self)
|
||||
{
|
||||
@@ -193,6 +195,18 @@ stpy_get_producer (PyObject *self, void *closure)
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/* Getter function for Symtab.compunit. */
|
||||
|
||||
static PyObject *
|
||||
stpy_get_compunit (PyObject *self, void *closure)
|
||||
{
|
||||
struct symtab *symtab = nullptr;
|
||||
|
||||
STPY_REQUIRE_VALID (self, symtab);
|
||||
|
||||
return compunit_to_compunit_object (symtab->compunit ()).release ();
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
stpy_fullname (PyObject *self, PyObject *args)
|
||||
{
|
||||
@@ -267,6 +281,46 @@ stpy_get_linetable (PyObject *self, PyObject *args)
|
||||
return symtab_to_linetable_object (self);
|
||||
}
|
||||
|
||||
/* Object initializer; creates new symtab.
|
||||
|
||||
Use: __init__(FILENAME, COMPUNIT). */
|
||||
|
||||
static int
|
||||
stpy_init (PyObject *zelf, PyObject *args, PyObject *kw)
|
||||
{
|
||||
struct symtab_object *self = (struct symtab_object*) zelf;
|
||||
|
||||
if (self->symtab)
|
||||
{
|
||||
PyErr_Format (PyExc_RuntimeError,
|
||||
_("Symtab object already initialized."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const char *keywords[] = { "filename", "compunit", nullptr };
|
||||
const char *filename;
|
||||
PyObject *cu_obj;
|
||||
|
||||
if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "sO", keywords,
|
||||
&filename, &cu_obj))
|
||||
return -1;
|
||||
|
||||
|
||||
compunit_symtab *cu = compunit_object_to_compunit (cu_obj);
|
||||
if (cu == nullptr)
|
||||
{
|
||||
PyErr_Format (PyExc_TypeError,
|
||||
_("The compunit argument is not valid gdb.Compunit "
|
||||
"object"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct symtab *symtab = allocate_symtab (cu, filename);
|
||||
set_symtab (self, symtab);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
salpy_str (PyObject *self)
|
||||
{
|
||||
@@ -511,7 +565,6 @@ symtab_object_to_symtab (PyObject *obj)
|
||||
static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
|
||||
gdbpy_initialize_symtabs (void)
|
||||
{
|
||||
symtab_object_type.tp_new = PyType_GenericNew;
|
||||
if (gdbpy_type_ready (&symtab_object_type) < 0)
|
||||
return -1;
|
||||
|
||||
@@ -533,6 +586,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 */
|
||||
};
|
||||
|
||||
@@ -579,13 +634,22 @@ PyTypeObject symtab_object_type = {
|
||||
"GDB symtab object", /*tp_doc */
|
||||
0, /*tp_traverse */
|
||||
0, /*tp_clear */
|
||||
0, /*tp_richcompare */
|
||||
gdbpy_richcompare<symtab_object, symtab, &symtab_object::symtab>,
|
||||
/*tp_richcompare */
|
||||
0, /*tp_weaklistoffset */
|
||||
0, /*tp_iter */
|
||||
0, /*tp_iternext */
|
||||
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[] = {
|
||||
|
||||
@@ -774,6 +774,57 @@ typy_unqualified (PyObject *self, PyObject *args)
|
||||
return type_to_type_object (type);
|
||||
}
|
||||
|
||||
/* Return a function type. */
|
||||
static PyObject *
|
||||
typy_function (PyObject *self, PyObject *args)
|
||||
{
|
||||
struct type *type = ((type_object *) self)->type;
|
||||
|
||||
gdb_assert (PySequence_Check (args));
|
||||
|
||||
std::vector<struct type *> param_types (PySequence_Length (args));
|
||||
|
||||
for (int i = 0; i < PySequence_Length (args); i++)
|
||||
{
|
||||
PyObject *param_type_obj = PySequence_GetItem (args, i);
|
||||
|
||||
if (param_type_obj == Py_None)
|
||||
{
|
||||
param_types[i] = nullptr;
|
||||
if (i != (PySequence_Length (args) - 1))
|
||||
{
|
||||
PyErr_Format (PyExc_ValueError,
|
||||
_("Argument at index %d is None but None can "
|
||||
"only be the last type."), i);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
param_types[i] = type_object_to_type (param_type_obj);
|
||||
if (!param_types[i])
|
||||
{
|
||||
PyErr_Format (PyExc_TypeError,
|
||||
_("Argument at index %d is not a gdb.Type "
|
||||
"object."), i);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
type = lookup_function_type_with_arguments (
|
||||
type, param_types.size (), param_types.data ());
|
||||
}
|
||||
catch (const gdb_exception &except)
|
||||
{
|
||||
return gdbpy_handle_gdb_exception (nullptr, except);
|
||||
}
|
||||
|
||||
return type_to_type_object (type);
|
||||
}
|
||||
|
||||
/* Return the size of the type represented by SELF, in bytes. */
|
||||
static PyObject *
|
||||
typy_get_sizeof (PyObject *self, void *closure)
|
||||
@@ -1641,6 +1692,9 @@ Return the type of a template argument." },
|
||||
{ "unqualified", typy_unqualified, METH_NOARGS,
|
||||
"unqualified () -> Type\n\
|
||||
Return a variant of this type without const or volatile attributes." },
|
||||
{ "function", typy_function, METH_VARARGS,
|
||||
"function () -> Type\n\
|
||||
Return a function type returning value of this type." },
|
||||
{ "values", typy_values, METH_NOARGS,
|
||||
"values () -> list\n\
|
||||
Return a list holding all the fields of this type.\n\
|
||||
|
||||
@@ -555,6 +555,8 @@ gdbpy_ref<thread_object> create_thread_object (struct thread_info *tp);
|
||||
gdbpy_ref<> thread_to_thread_object (thread_info *thr);;
|
||||
gdbpy_ref<inferior_object> inferior_to_inferior_object (inferior *inf);
|
||||
|
||||
gdbpy_ref<> compunit_to_compunit_object (struct compunit_symtab *compunit);
|
||||
|
||||
PyObject *gdbpy_buffer_to_membuf (gdb::unique_xmalloc_ptr<gdb_byte> buffer,
|
||||
CORE_ADDR address, ULONGEST length);
|
||||
|
||||
@@ -571,6 +573,9 @@ 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);
|
||||
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);
|
||||
@@ -1145,4 +1150,37 @@ gdbpy_type_ready (PyTypeObject *type, PyObject *mod = nullptr)
|
||||
# define PyType_Ready POISONED_PyType_Ready
|
||||
#endif
|
||||
|
||||
/* Implements default equality and non-equality comparisons for GDB
|
||||
Python objects.
|
||||
|
||||
All other comparison operators will throw a TypeError Python exception.
|
||||
|
||||
Two Python objects of type P are considered equal if both point to the
|
||||
same underlying GBB structure of type D. The last template parameter
|
||||
specifies the member of Python object holding reference to underlying
|
||||
GBB structure. */
|
||||
|
||||
template <typename P, typename D, D* P::*data>
|
||||
PyObject *
|
||||
gdbpy_richcompare (PyObject *self, PyObject *other, int op)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (!PyObject_TypeCheck (other, self->ob_type)
|
||||
|| (op != Py_EQ && op != Py_NE))
|
||||
{
|
||||
Py_INCREF (Py_NotImplemented);
|
||||
return Py_NotImplemented;
|
||||
}
|
||||
|
||||
if ( (P *)self->*data == (P *)other->*data)
|
||||
result = Py_EQ;
|
||||
else
|
||||
result = Py_NE;
|
||||
|
||||
if (op == result)
|
||||
Py_RETURN_TRUE;
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
|
||||
#endif /* PYTHON_PYTHON_INTERNAL_H */
|
||||
|
||||
@@ -229,6 +229,15 @@ proc jit_reader_test {} {
|
||||
gdb_test "python print( \[o for o in gdb.objfiles() if o.filename.startswith('<< JIT compiled code')\]\[0\].build_id )" \
|
||||
"None" \
|
||||
"python gdb.Objfile.build_id"
|
||||
|
||||
# Check that Progspace.objfile_for_address () finds "jitted"
|
||||
# objfile
|
||||
gdb_test "frame 0" \
|
||||
"#0 $hex in jit_function_stack_mangle ()$any" \
|
||||
"select frame 0"
|
||||
gdb_test "python print( gdb.current_progspace().objfile_for_address(gdb.parse_and_eval('\$pc')) )" \
|
||||
"<gdb.Objfile filename=<< JIT compiled code at $hex >>>" \
|
||||
"python gdb.Progspace.objfile_for_address"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,6 +104,10 @@ foreach_with_prefix test_data { {None None} \
|
||||
"check 'signed' argument can handle non-bool type $bad_type"
|
||||
}
|
||||
|
||||
gdb_test "python print(arch.void_type())" \
|
||||
"void" \
|
||||
"get void type"
|
||||
|
||||
# Test for gdb.architecture_names(). First we're going to grab the
|
||||
# complete list of architecture names using the 'complete' command.
|
||||
set arch_names []
|
||||
|
||||
@@ -61,6 +61,10 @@ gdb_py_test_silent_cmd "python sblock = block.static_block" \
|
||||
"Get block, static_block" 1
|
||||
gdb_test "python print (gblock.is_global)" "True" "is the global block"
|
||||
gdb_test "python print (sblock.is_static)" "True" "is the static block"
|
||||
gdb_test "python print (len(gblock.subblocks) > 0)" "True" \
|
||||
"global block contains at least one block"
|
||||
gdb_test "python print (sblock in gblock.subblocks)" "True" \
|
||||
"global block contains at static block"
|
||||
|
||||
# Move up superblock(s) until we reach function block_func.
|
||||
gdb_test_no_output "python block = block.superblock" "get superblock"
|
||||
@@ -100,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" ".*"
|
||||
@@ -109,6 +115,49 @@ 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 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
|
||||
|
||||
102
gdb/testsuite/gdb.python/py-compunit.exp
Normal file
102
gdb/testsuite/gdb.python/py-compunit.exp
Normal file
@@ -0,0 +1,102 @@
|
||||
# Copyright (C) 2024-2024 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# This file is part of the GDB testsuite. It tests the compunit
|
||||
# support in Python.
|
||||
|
||||
load_lib gdb-python.exp
|
||||
|
||||
require allow_python_tests
|
||||
|
||||
standard_testfile py-objfile.c
|
||||
|
||||
if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } {
|
||||
return -1
|
||||
}
|
||||
|
||||
if {![runto_main]} {
|
||||
return 0
|
||||
}
|
||||
|
||||
set python_error_text "Error occurred in Python.*"
|
||||
|
||||
gdb_py_test_silent_cmd "python sym = gdb.lookup_symbol(\"some_var\")" \
|
||||
"Find a symbol in objfile" 1
|
||||
gdb_py_test_silent_cmd "python objfile = sym\[0\].symtab.objfile" \
|
||||
"Get backing object file" 1
|
||||
|
||||
gdb_test "python print (len(objfile.compunits()) > 0)" \
|
||||
"True" \
|
||||
"Get objfile compunits"
|
||||
gdb_test "python print (objfile.compunits())" \
|
||||
"\\\[<gdb\.Compunit object at .*>\\\]" \
|
||||
"Objfile compunits return a sequence of gdb.Compunit"
|
||||
gdb_test "python print (objfile.compunits()\[0\] == objfile.compunits()\[0\])" \
|
||||
"True" \
|
||||
"Compunits are comparable"
|
||||
gdb_test "python print (len(objfile.compunits()\[0\].symtabs) > 0)" \
|
||||
"True" \
|
||||
"Get compunit symtabs"
|
||||
gdb_test "python print (objfile.compunits()\[0\].symtabs)" \
|
||||
"\\\[<gdb\.Symtab.*>\\\]" \
|
||||
"Compunit symtabs return a sequence of gdb.Symtab"
|
||||
|
||||
|
||||
# Test creation of compunits
|
||||
gdb_py_test_silent_cmd "python cu = gdb.Compunit(\"compunit1\", objfile, 200, 300)" \
|
||||
"Create compunit1" 1
|
||||
gdb_test "python print(cu)" \
|
||||
"<gdb\.Compunit object at .*>" \
|
||||
"Print created compunit1"
|
||||
gdb_test "python print(cu in objfile.compunits())" \
|
||||
"True" \
|
||||
"Test compunit1 is in objfile.compunits()"
|
||||
gdb_test "python print(cu.global_block().start)" \
|
||||
"0" \
|
||||
"Test compunit1.global_block().start is 0"
|
||||
gdb_test "python print(cu.global_block().end)" \
|
||||
"0" \
|
||||
"Test compunit1.global_block().end is 0"
|
||||
gdb_test "python print(cu.static_block().start)" \
|
||||
"0" \
|
||||
"Test compunit1.static_block().start is 0"
|
||||
gdb_test "python print(cu.static_block().end)" \
|
||||
"0" \
|
||||
"Test compunit1.static_block().end is 0"
|
||||
|
||||
gdb_py_test_silent_cmd "python cu2 = gdb.Compunit(\"dynamic2\", objfile, 400, 500, 24)" \
|
||||
"Create compunit2 with capacity fo 24 blocks" 1
|
||||
|
||||
gdb_test "python cu3 = gdb.Compunit(\"dynamic3\", gdb, 600, 700)" \
|
||||
"TypeError.*:.*" \
|
||||
"Create compunit3 passing non-objfile"
|
||||
|
||||
gdb_test "python cu4 = gdb.Compunit(\"dynamic4\", objfile, 900, 800)" \
|
||||
"ValueError.*:.*" \
|
||||
"Create compunit4 passing invalid global block range"
|
||||
|
||||
gdb_test "python cu5 = gdb.Compunit(\"dynamic5\", objfile, 225, 325)" \
|
||||
"ValueError.*:.*" \
|
||||
"Create compunit4 passing overlapping global block range"
|
||||
|
||||
|
||||
gdb_unload "unload 1"
|
||||
|
||||
gdb_test "python print (objfile.is_valid())" "False" \
|
||||
"Get objfile validity after unload"
|
||||
gdb_test "python print (objfile.compunits())" "RuntimeError.*: Objfile no longer exists.*" \
|
||||
"Get objfile compunits after unload"
|
||||
gdb_py_test_silent_cmd "python cu6 = gdb.Compunit(\"dynamic6\", objfile)" \
|
||||
"Create compunit4 passing invalid objfile" 1
|
||||
61
gdb/testsuite/gdb.python/py-jit.c
Normal file
61
gdb/testsuite/gdb.python/py-jit.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/* Copyright (C) 2024-2024 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GDB.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
/* "JIT-ed" function, with the prototype `long (long, long)`. */
|
||||
static const unsigned char jit_function_add_code[] = {
|
||||
0x48, 0x01, 0xfe, /* add %rdi,%rsi */
|
||||
0x48, 0x89, 0xf0, /* mov %rsi,%rax */
|
||||
0xc3, /* retq */
|
||||
};
|
||||
|
||||
/* Dummy function to inform the debugger a new code has been installed. */
|
||||
void jit_register_code (char * name, uintptr_t code, size_t size)
|
||||
{}
|
||||
|
||||
/* Dummy function to inform the debugger that code has been installed. */
|
||||
void jit_unregister_code (uintptr_t code)
|
||||
{}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
void *code = mmap (NULL, getpagesize (), PROT_WRITE | PROT_EXEC,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
assert (code != MAP_FAILED);
|
||||
|
||||
/* "Compile" jit_function_add. */
|
||||
memcpy (code, jit_function_add_code,
|
||||
sizeof (jit_function_add_code));
|
||||
jit_register_code ("jit_function_add", (uintptr_t)code, sizeof (jit_function_add_code));
|
||||
|
||||
((long (*)(long, long))code)(1,5); /* breakpoint 1 line */
|
||||
|
||||
/* "Discard" jit_function_add. */
|
||||
memset(code, 0, sizeof(jit_function_add_code));
|
||||
jit_unregister_code ((uintptr_t)code);
|
||||
|
||||
return 0; /* breakpoint 2 line */
|
||||
}
|
||||
57
gdb/testsuite/gdb.python/py-jit.exp
Normal file
57
gdb/testsuite/gdb.python/py-jit.exp
Normal file
@@ -0,0 +1,57 @@
|
||||
# Copyright (C) 2024-2024 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# This file is part of the GDB testsuite. It test the Python API to
|
||||
# create symbol tables for dynamic (JIT) code and follows the example
|
||||
# code given in documentation (see section JIT Interface in Python)
|
||||
|
||||
load_lib gdb-python.exp
|
||||
|
||||
require allow_python_tests
|
||||
require is_x86_64_m64_target
|
||||
|
||||
standard_testfile
|
||||
|
||||
if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } {
|
||||
return -1
|
||||
}
|
||||
|
||||
if ![runto_main] {
|
||||
return 0
|
||||
}
|
||||
|
||||
set remote_python_file [gdb_remote_download host \
|
||||
${srcdir}/${subdir}/${testfile}.py]
|
||||
gdb_test_no_output "source ${remote_python_file}" "load python file"
|
||||
|
||||
gdb_breakpoint [gdb_get_line_number "breakpoint 1 line" ${testfile}.c]
|
||||
gdb_continue_to_breakpoint "continue to breakpoint 1 line"
|
||||
gdb_test "disassemble /s jit_function_add" \
|
||||
"Dump of assembler code for function jit_function_add:.*End of assembler dump." \
|
||||
"disassemble jit_function_add"
|
||||
|
||||
gdb_breakpoint "jit_function_add"
|
||||
gdb_continue_to_breakpoint "continue to jit_function_add"
|
||||
|
||||
gdb_test "bt 1" \
|
||||
"#0 jit_function_add \\(\\) at py-jit.c:.*" \
|
||||
"bt 1"
|
||||
|
||||
gdb_breakpoint [gdb_get_line_number "breakpoint 2 line" ${testfile}.c]
|
||||
gdb_continue_to_breakpoint "continue to breakpoint 2 line"
|
||||
|
||||
gdb_test "disassemble jit_function_add" \
|
||||
"No symbol \"jit_function_add\" in current context." \
|
||||
"disassemble jit_function_add after code has been unregistered"
|
||||
110
gdb/testsuite/gdb.python/py-jit.py
Normal file
110
gdb/testsuite/gdb.python/py-jit.py
Normal file
@@ -0,0 +1,110 @@
|
||||
# Copyright (C) 2024-2024 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is part of GDB.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
# This code is same (modulo small tweaks) as the code in documentation,
|
||||
# section "JIT Interface in Python". If changed the documentation should
|
||||
# be checked and updated accordingly if necessary.
|
||||
import gdb
|
||||
|
||||
objfile = None
|
||||
compunit = None
|
||||
block = None
|
||||
symtab = None
|
||||
symbol = None
|
||||
|
||||
def get_linetable_entries(addr):
|
||||
# Entries are not in increasing order to test that
|
||||
# gdb.LineTable.__init__() sorts them properly.
|
||||
return [
|
||||
gdb.LineTableEntry(31, addr+6, True),
|
||||
gdb.LineTableEntry(29, addr, True),
|
||||
gdb.LineTableEntry(30, addr+3, True)
|
||||
]
|
||||
|
||||
|
||||
class JITRegisterCode(gdb.Breakpoint):
|
||||
def stop(self):
|
||||
|
||||
global objfile
|
||||
global compunit
|
||||
global block
|
||||
global symtab
|
||||
global symbol
|
||||
|
||||
frame = gdb.newest_frame()
|
||||
name = frame.read_var('name').string()
|
||||
addr = int(frame.read_var('code'))
|
||||
size = int(frame.read_var('size'))
|
||||
linetable_entries = get_linetable_entries(addr)
|
||||
|
||||
# Create objfile and compunit for allocated "jit" code
|
||||
objfile = gdb.Objfile(name)
|
||||
compunit = gdb.Compunit(name, objfile, addr, addr + size)
|
||||
|
||||
# Mark the objfile as "jitted code". This will be used later when
|
||||
# unregistering discarded code to check the objfile was indeed
|
||||
# created for jitted code.
|
||||
setattr(objfile, "is_jit_code", True)
|
||||
|
||||
# Create block for jitted function
|
||||
block = gdb.Block(compunit.static_block(), addr, addr + size)
|
||||
|
||||
# Create symbol table holding info about jitted function, ...
|
||||
symtab = gdb.Symtab("py-jit.c", compunit)
|
||||
linetable = gdb.LineTable(symtab, linetable_entries)
|
||||
|
||||
# ...type of the jitted function...
|
||||
int64_t = gdb.selected_inferior().architecture().integer_type(64)
|
||||
func_t = int64_t.function(int64_t, int64_t)
|
||||
|
||||
# ...and symbol representing jitted function.
|
||||
symbol = gdb.Symbol(name, symtab, func_t,
|
||||
gdb.SYMBOL_FUNCTION_DOMAIN, gdb.SYMBOL_LOC_BLOCK,
|
||||
block)
|
||||
|
||||
# Finally, register the symbol in static block
|
||||
compunit.static_block().add_symbol(symbol)
|
||||
|
||||
return False # Continue execution
|
||||
|
||||
# Create breakpoint to register new code
|
||||
JITRegisterCode("jit_register_code", internal=True)
|
||||
|
||||
|
||||
class JITUnregisterCode(gdb.Breakpoint):
|
||||
def stop(self):
|
||||
frame = gdb.newest_frame()
|
||||
addr = int(frame.read_var('code'))
|
||||
|
||||
objfile = gdb.current_progspace().objfile_for_address(addr)
|
||||
if objfile is None:
|
||||
# No objfile for given addr - bail out (this should not happen)
|
||||
assert False
|
||||
return False # Continue execution
|
||||
|
||||
if not getattr(objfile, "is_jit_code", False):
|
||||
# Not a jitted addr - bail out (this should not happen either)
|
||||
assert False
|
||||
return False # Continue execution
|
||||
|
||||
# Remove the objfile and all debug info associated with it.
|
||||
objfile.unlink()
|
||||
|
||||
return False # Continue execution
|
||||
|
||||
# Create breakpoint to discard old code
|
||||
JITUnregisterCode("jit_unregister_code", internal=True)
|
||||
@@ -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" \
|
||||
@@ -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()" \
|
||||
@@ -71,3 +81,56 @@ 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"
|
||||
|
||||
# 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"
|
||||
|
||||
@@ -172,4 +172,52 @@ 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"
|
||||
|
||||
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"
|
||||
|
||||
@@ -135,6 +135,7 @@ gdb_test "python print (func.name)" "func" "test func.name"
|
||||
gdb_test "python print (func.print_name)" "func" "test func.print_name"
|
||||
gdb_test "python print (func.linkage_name)" "func" "test func.linkage_name"
|
||||
gdb_test "python print (func.addr_class == gdb.SYMBOL_LOC_BLOCK)" "True" "test func.addr_class"
|
||||
gdb_test "python print (func.domain == gdb.SYMBOL_FUNCTION_DOMAIN)" "True" "test func.domain"
|
||||
|
||||
# Stop in a second file and ensure we find its local static symbol.
|
||||
gdb_breakpoint "function_in_other_file"
|
||||
@@ -200,6 +201,40 @@ if { [is_remote host] } {
|
||||
}
|
||||
gdb_test "python print (t\[0\].symtab)" "${py_symbol_c}" "get symtab"
|
||||
|
||||
# Test comparison for equality and non-equality
|
||||
gdb_test "python print (t\[0\] == t\[0\] )"\
|
||||
"True" \
|
||||
"test symbol equality with itself"
|
||||
gdb_test "python print (t\[0\] == a\[0\] )"\
|
||||
"False" \
|
||||
"test symbol equality with other symbol"
|
||||
gdb_test "python print (t\[0\] == 123 )"\
|
||||
"False" \
|
||||
"test symbol equality with non-symbol"
|
||||
|
||||
gdb_test "python print (t\[0\] != t\[0\] )"\
|
||||
"False" \
|
||||
"test symbol non-equality with itself"
|
||||
gdb_test "python print (t\[0\] != a\[0\] )"\
|
||||
"True" \
|
||||
"test symbol non-equality with other symbol"
|
||||
gdb_test "python print (t\[0\] != 123 )"\
|
||||
"True" \
|
||||
"test symbol non-equality with non-symbol"
|
||||
|
||||
# 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++
|
||||
|
||||
@@ -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,44 @@ gdb_test_multiple "python print (\"simple_struct\" in static_symbols)" \
|
||||
}
|
||||
}
|
||||
}
|
||||
# Test comparison for equality and non-equality
|
||||
gdb_test "python print (symtab == symtab)"\
|
||||
"True" \
|
||||
"test symtab equality with itself"
|
||||
gdb_test "python print (symtab == sal.symtab)"\
|
||||
"True" \
|
||||
"test symtab equality with other symtab object referring to the same symtab"
|
||||
gdb_test "python print (symtab == 123 )"\
|
||||
"False" \
|
||||
"test symtab equality with non-symtab"
|
||||
|
||||
gdb_test "python print (symtab != symtab)"\
|
||||
"False" \
|
||||
"test symtab non-equality with itself"
|
||||
gdb_test "python print (symtab != sal.symtab)"\
|
||||
"False" \
|
||||
"test symtab non-equality with other symtab object referring to the same symtab"
|
||||
gdb_test "python print (symtab != 123 )"\
|
||||
"True" \
|
||||
"test symtab non-equality with non-symtab"
|
||||
|
||||
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 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.
|
||||
@@ -100,3 +140,7 @@ gdb_test "python print (symtab.is_valid())" "False" \
|
||||
|
||||
gdb_test_no_output "python sal = None" "test sal destructor"
|
||||
gdb_test_no_output "python symtab = None" "test symtab destructor"
|
||||
|
||||
gdb_test "python print(repr( gdb.Symtab(\"some_file2.txt\", cu) ))" \
|
||||
"TypeError.*:.*" \
|
||||
"test creation of symtab passing invalid compunit"
|
||||
|
||||
@@ -365,6 +365,26 @@ if { [build_inferior "${binfile}" "c"] == 0 } {
|
||||
gdb_test "python print(gdb.lookup_type('int').optimized_out())" \
|
||||
"<optimized out>"
|
||||
|
||||
gdb_test_no_output "python int_t = gdb.lookup_type('int')"
|
||||
|
||||
gdb_test "python print(repr(int_t.function()))" \
|
||||
"<gdb.Type code=TYPE_CODE_FUNC name=int \\(\\)>"
|
||||
|
||||
gdb_test "python print(repr(int_t.function(int_t, int_t, int_t)))" \
|
||||
"<gdb.Type code=TYPE_CODE_FUNC name=int \\(int, int, int\\)>"
|
||||
|
||||
gdb_test "python print(repr(int_t.function(int_t, None)))" \
|
||||
"<gdb.Type code=TYPE_CODE_FUNC name=int \\(int, ...\\)>"
|
||||
|
||||
gdb_test "python print(repr(int_t.function(None)))" \
|
||||
"<gdb.Type code=TYPE_CODE_FUNC name=int \\(\\)>"
|
||||
|
||||
gdb_test "python print(repr(int_t.function(123)))" \
|
||||
"TypeError.*:.*"
|
||||
|
||||
gdb_test "python print(repr(int_t.function(int_t, None, int_t)))" \
|
||||
"ValueError.*:.*"
|
||||
|
||||
set sint [get_sizeof int 0]
|
||||
gdb_test "python print(gdb.parse_and_eval('aligncheck').type.alignof)" \
|
||||
$sint
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#if !defined (GDB_OBSTACK_H)
|
||||
#define GDB_OBSTACK_H 1
|
||||
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include "obstack.h"
|
||||
|
||||
/* Utility macros - wrap obstack alloc into something more robust. */
|
||||
@@ -157,4 +159,54 @@ struct allocate_on_obstack
|
||||
void operator delete[] (void *memory) {}
|
||||
};
|
||||
|
||||
/* Implementation of Allocator concept using obstack to
|
||||
allocate memory. This allows standard containers to be
|
||||
used with obstack. */
|
||||
|
||||
template <typename T>
|
||||
class obstack_allocator
|
||||
{
|
||||
public:
|
||||
typedef T value_type;
|
||||
|
||||
obstack_allocator (struct obstack *obstack)
|
||||
: m_obstack(obstack)
|
||||
{}
|
||||
|
||||
template <typename U> constexpr obstack_allocator (const obstack_allocator<U>& allocator) noexcept
|
||||
: m_obstack(allocator.m_obstack)
|
||||
{}
|
||||
|
||||
T* allocate (std::size_t n)
|
||||
{
|
||||
if (n > std::numeric_limits<std::size_t>::max () / sizeof (T))
|
||||
throw std::bad_array_new_length ();
|
||||
|
||||
if (auto p = static_cast<T*> (obstack_alloc (m_obstack, n * sizeof (T))))
|
||||
{
|
||||
return p;
|
||||
}
|
||||
|
||||
throw std::bad_alloc ();
|
||||
}
|
||||
|
||||
void deallocate(T* p, std::size_t n) noexcept
|
||||
{}
|
||||
|
||||
private:
|
||||
|
||||
struct obstack *m_obstack;
|
||||
};
|
||||
|
||||
template <class T, class U>
|
||||
bool operator==(const obstack_allocator<T> &t, const obstack_allocator<U> &u)
|
||||
{
|
||||
return (std::is_same<T, U>::value_type) && (t.m_obstack == u.m_obstack);
|
||||
}
|
||||
template <class T, class U>
|
||||
bool operator!=(const obstack_allocator<T> &t, const obstack_allocator<U> &u)
|
||||
{
|
||||
return ! (t == u);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user