Files
binutils-gdb/gdb/python/py-linetable.c
Jan Vrany 171a85e261 gdb/python: allow instantiation of gdb.LineTable objects
This commit allows users to instantiate gdb.LineTable objects.
This is a step towards a Python support for dynamically generated code
(JIT) in GDB.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
2024-11-21 13:52:21 +00:00

792 lines
22 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Python interface to line tables.
Copyright (C) 2013-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 "python-internal.h"
#include "objfiles.h"
struct linetable_entry_object {
PyObject_HEAD
/* The line table source line. */
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
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("linetable_entry_object");
struct linetable_object {
PyObject_HEAD
/* The symtab python object. We store the Python object here as the
underlying symtab can become invalid, and we have to run validity
checks on it. */
PyObject *symtab;
};
extern PyTypeObject linetable_object_type
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("linetable_object");
struct ltpy_iterator_object {
PyObject_HEAD
/* The current entry in the line table for the iterator */
int current_index;
/* Pointer back to the original source line table object. Needed to
check if the line table is still valid, and has not been invalidated
when an object file has been freed. */
PyObject *source;
};
extern PyTypeObject ltpy_iterator_object_type
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("ltpy_iterator_object");
/* Internal helper function to extract gdb.Symtab from a gdb.LineTable
object. */
static PyObject *
get_symtab (PyObject *linetable)
{
linetable_object *lt = (linetable_object *) linetable;
return lt->symtab;
}
#define LTPY_REQUIRE_VALID(lt_obj, symtab) \
do { \
symtab = symtab_object_to_symtab (get_symtab (lt_obj)); \
if (symtab == NULL) \
{ \
PyErr_SetString (PyExc_RuntimeError, \
_("Symbol Table in line table is invalid."));\
return NULL; \
} \
} while (0)
/* Helper function to create a line table object that wraps a
gdb.Symtab object. */
PyObject *
symtab_to_linetable_object (PyObject *symtab)
{
linetable_object *ltable;
ltable = PyObject_New (linetable_object, &linetable_object_type);
if (ltable != NULL)
{
ltable->symtab = symtab;
Py_INCREF (symtab);
}
return (PyObject *) ltable;
}
/* Internal helper function to build a line table object from a line
and an address. */
static PyObject *
build_linetable_entry (int line, CORE_ADDR address, bool is_stmt,
bool prologue_end, bool epilogue_begin)
{
linetable_entry_object *obj;
obj = PyObject_New (linetable_entry_object,
&linetable_entry_object_type);
if (obj != NULL)
{
obj->line = line;
obj->pc = address;
obj->is_stmt = is_stmt;
obj->prologue_end = prologue_end;
obj->epilogue_begin = epilogue_begin;
}
return (PyObject *) obj;
}
/* Internal helper function to build a Python Tuple from a vector.
A line table entry can have multiple PCs for a given source line.
Construct a Tuple of all entries for the given source line, LINE
from the line table PCS. Construct one line table entry object per
address. */
static PyObject *
build_line_table_tuple_from_entries (
const struct objfile *objfile,
const std::vector<const linetable_entry *> &entries)
{
int i;
if (entries.size () < 1)
Py_RETURN_NONE;
gdbpy_ref<> tuple (PyTuple_New (entries.size ()));
if (tuple == NULL)
return NULL;
for (i = 0; i < entries.size (); ++i)
{
const linetable_entry *entry = entries[i];
gdbpy_ref<> obj (build_linetable_entry (
entry->line, entry->pc (objfile), entry->is_stmt,
entry->prologue_end, entry->epilogue_begin));
if (obj == NULL)
return NULL;
else if (PyTuple_SetItem (tuple.get (), i, obj.release ()) != 0)
return NULL;
}
return tuple.release ();
}
/* Implementation of gdb.LineTable.line (self) -> Tuple. Returns a
tuple of LineTableEntry objects associated with this line from the
in the line table. */
static PyObject *
ltpy_get_pcs_for_line (PyObject *self, PyObject *args)
{
struct symtab *symtab;
gdb_py_longest py_line;
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
{
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);
}
struct objfile *objfile = symtab->compunit ()->objfile ();
return build_line_table_tuple_from_entries (objfile, entries);
}
/* Implementation of gdb.LineTable.has_line (self, line) -> Boolean.
Returns a Python Boolean indicating whether a source line has any
line table entries corresponding to it. */
static PyObject *
ltpy_has_line (PyObject *self, PyObject *args)
{
struct symtab *symtab;
gdb_py_longest py_line;
int index;
LTPY_REQUIRE_VALID (self, symtab);
if (! PyArg_ParseTuple (args, GDB_PY_LL_ARG, &py_line))
return NULL;
if (symtab->linetable () == NULL)
{
PyErr_SetString (PyExc_RuntimeError,
_("Linetable information not found in symbol table"));
return NULL;
}
for (index = 0; index < symtab->linetable ()->nitems; index++)
{
const linetable_entry *item = &(symtab->linetable ()->item[index]);
if (item->line == py_line)
Py_RETURN_TRUE;
}
Py_RETURN_FALSE;
}
/* Implementation of gdb.LineTable.source_lines (self) -> List.
Returns a Python List that contains source line entries in the
line table. This function will just return the source lines
without corresponding addresses. */
static PyObject *
ltpy_get_all_source_lines (PyObject *self, PyObject *args)
{
struct symtab *symtab;
Py_ssize_t index;
LTPY_REQUIRE_VALID (self, symtab);
if (symtab->linetable () == NULL)
{
PyErr_SetString (PyExc_RuntimeError,
_("Linetable information not found in symbol table"));
return NULL;
}
gdbpy_ref<> source_dict (PyDict_New ());
if (source_dict == NULL)
return NULL;
for (index = 0; index < symtab->linetable ()->nitems; index++)
{
const linetable_entry *item = &(symtab->linetable ()->item[index]);
/* 0 is used to signify end of line table information. Do not
include in the source set. */
if (item->line > 0)
{
gdbpy_ref<> line = gdb_py_object_from_longest (item->line);
if (line == NULL)
return NULL;
if (PyDict_SetItem (source_dict.get (), line.get (), Py_None) == -1)
return NULL;
}
}
return PyDict_Keys (source_dict.get ());
}
/* Implementation of gdb.LineTable.is_valid (self) -> Boolean.
Returns True if this line table object still exists in GDB. */
static PyObject *
ltpy_is_valid (PyObject *self, PyObject *args)
{
struct symtab *symtab = NULL;
symtab = symtab_object_to_symtab (get_symtab (self));
if (symtab == NULL)
Py_RETURN_FALSE;
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. */
static void
ltpy_dealloc (PyObject *self)
{
linetable_object *obj = (linetable_object *) self;
if (obj->symtab)
Py_DECREF (obj->symtab);
Py_TYPE (self)->tp_free (self);
}
/* Initialize LineTable, LineTableEntry and LineTableIterator
objects. */
static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
gdbpy_initialize_linetable (void)
{
if (gdbpy_type_ready (&linetable_object_type) < 0)
return -1;
if (gdbpy_type_ready (&linetable_entry_object_type) < 0)
return -1;
if (gdbpy_type_ready (&ltpy_iterator_object_type) < 0)
return -1;
return 0;
}
/* LineTable entry object get functions. */
/* Implementation of gdb.LineTableEntry.line (self) -> Long. Returns
a long integer associated with the line table entry. */
static PyObject *
ltpy_entry_get_line (PyObject *self, void *closure)
{
linetable_entry_object *obj = (linetable_entry_object *) self;
return gdb_py_object_from_longest (obj->line).release ();
}
/* Implementation of gdb.LineTableEntry.pc (self) -> Long. Returns a
a long integer associated with the PC of the line table entry. */
static PyObject *
ltpy_entry_get_pc (PyObject *self, void *closure)
{
linetable_entry_object *obj = (linetable_entry_object *) self;
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. */
static PyObject *
ltpy_iter (PyObject *self)
{
ltpy_iterator_object *ltpy_iter_obj;
struct symtab *symtab = NULL;
LTPY_REQUIRE_VALID (self, symtab);
ltpy_iter_obj = PyObject_New (ltpy_iterator_object,
&ltpy_iterator_object_type);
if (ltpy_iter_obj == NULL)
return NULL;
ltpy_iter_obj->current_index = 0;
ltpy_iter_obj->source = self;
Py_INCREF (self);
return (PyObject *) ltpy_iter_obj;
}
static void
ltpy_iterator_dealloc (PyObject *obj)
{
ltpy_iterator_object *iter_obj = (ltpy_iterator_object *) obj;
Py_DECREF (iter_obj->source);
Py_TYPE (obj)->tp_free (obj);
}
/* Return a reference to the line table iterator. */
static PyObject *
ltpy_iterator (PyObject *self)
{
ltpy_iterator_object *iter_obj = (ltpy_iterator_object *) self;
struct symtab *symtab;
LTPY_REQUIRE_VALID (iter_obj->source, symtab);
Py_INCREF (self);
return self;
}
/* Return the next line table entry in the iteration through the line
table data structure. */
static PyObject *
ltpy_iternext (PyObject *self)
{
ltpy_iterator_object *iter_obj = (ltpy_iterator_object *) self;
struct symtab *symtab;
PyObject *obj;
LTPY_REQUIRE_VALID (iter_obj->source, symtab);
if (symtab->linetable () == nullptr
|| iter_obj->current_index >= symtab->linetable ()->nitems)
{
PyErr_SetNone (PyExc_StopIteration);
return NULL;
}
const linetable_entry *item
= &(symtab->linetable ()->item[iter_obj->current_index]);
/* Skip over internal entries such as 0. 0 signifies the end of
line table data and is not useful to the API user. */
while (item->line < 1)
{
iter_obj->current_index++;
/* Exit if the internal value is the last item in the line table. */
if (iter_obj->current_index >= symtab->linetable ()->nitems)
{
PyErr_SetNone (PyExc_StopIteration);
return NULL;
}
item = &(symtab->linetable ()->item[iter_obj->current_index]);
}
struct objfile *objfile = symtab->compunit ()->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;
}
/* Implementation of gdb.LineTableIterator.is_valid (self) -> Boolean.
Returns True if this line table iterator object still exists in
GDB. */
static PyObject *
ltpy_iter_is_valid (PyObject *self, PyObject *args)
{
struct symtab *symtab = NULL;
ltpy_iterator_object *iter_obj = (ltpy_iterator_object *) self;
symtab = symtab_object_to_symtab (get_symtab (iter_obj->source));
if (symtab == NULL)
Py_RETURN_FALSE;
Py_RETURN_TRUE;
}
GDBPY_INITIALIZE_FILE (gdbpy_initialize_linetable);
static PyMethodDef linetable_object_methods[] = {
{ "line", ltpy_get_pcs_for_line, METH_VARARGS,
"line (lineno) -> Tuple\n\
Return executable locations for a given source line." },
{ "has_line", ltpy_has_line, METH_VARARGS,
"has_line (lineno) -> Boolean\n\
Return TRUE if this line has executable information, FALSE if not." },
{ "source_lines", ltpy_get_all_source_lines, METH_NOARGS,
"source_lines () -> List\n\
Return a list of all executable source lines." },
{ "is_valid", ltpy_is_valid, METH_NOARGS,
"is_valid () -> Boolean.\n\
Return True if this LineTable is valid, False if not." },
{NULL} /* Sentinel */
};
PyTypeObject linetable_object_type = {
PyVarObject_HEAD_INIT (NULL, 0)
"gdb.LineTable", /*tp_name*/
sizeof (linetable_object), /*tp_basicsize*/
0, /*tp_itemsize*/
ltpy_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 line table object", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
ltpy_iter, /* tp_iter */
0, /* tp_iternext */
linetable_object_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
ltpy_init, /* tp_init */
0, /* tp_alloc */
PyType_GenericNew /* tp_new */
};
static PyMethodDef ltpy_iterator_methods[] = {
{ "is_valid", ltpy_iter_is_valid, METH_NOARGS,
"is_valid () -> Boolean.\n\
Return True if this LineTable iterator is valid, False if not." },
{NULL} /* Sentinel */
};
PyTypeObject ltpy_iterator_object_type = {
PyVarObject_HEAD_INIT (NULL, 0)
"gdb.LineTableIterator", /*tp_name*/
sizeof (ltpy_iterator_object), /*tp_basicsize*/
0, /*tp_itemsize*/
ltpy_iterator_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 line table iterator object", /*tp_doc */
0, /*tp_traverse */
0, /*tp_clear */
0, /*tp_richcompare */
0, /*tp_weaklistoffset */
ltpy_iterator, /*tp_iter */
ltpy_iternext, /*tp_iternext */
ltpy_iterator_methods /*tp_methods */
};
static gdb_PyGetSetDef linetable_entry_object_getset[] = {
{ "line", ltpy_entry_get_line, NULL,
"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*/
sizeof (linetable_entry_object), /*tp_basicsize*/
0, /*tp_itemsize*/
0, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"GDB line table entry object", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
linetable_entry_object_getset, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
ltpy_entry_init, /* tp_init */
0, /* tp_alloc */
PyType_GenericNew, /* tp_new */
};