diff options
author | Andrew Burgess <aburgess@redhat.com> | 2023-03-08 16:11:45 +0000 |
---|---|---|
committer | Andrew Burgess <aburgess@redhat.com> | 2023-03-30 10:25:46 +0100 |
commit | 86b35b7116a325ae0179e5b74692e4e7a1e8b2d7 (patch) | |
tree | 0ce8f9a3a3415d18be9780df12c53f97e5d3dd8e /gdb/python/py-unwind.c | |
parent | 44d9b0a174b08f283001f01aaf84102ba0a2726a (diff) | |
download | binutils-86b35b7116a325ae0179e5b74692e4e7a1e8b2d7.zip binutils-86b35b7116a325ae0179e5b74692e4e7a1e8b2d7.tar.gz binutils-86b35b7116a325ae0179e5b74692e4e7a1e8b2d7.tar.bz2 |
gdb/python: add some additional methods to gdb.PendingFrame
The gdb.Frame class has far more methods than gdb.PendingFrame. Given
that a PendingFrame hasn't yet been claimed by an unwinder, there is a
limit to which methods we can add to it, but many of the methods that
the Frame class has, the PendingFrame class could also support.
In this commit I've added those methods to PendingFrame that I believe
are safe.
In terms of implementation: if I was starting from scratch then I
would implement many of these (or most of these) as attributes rather
than methods. However, given both Frame and PendingFrame are just
different representation of a frame, I think there is value in keeping
the interface for the two classes the same. For this reason
everything here is a method -- that's what the Frame class does.
The new methods I've added are:
- gdb.PendingFrame.is_valid: Return True if the pending frame
object is valid.
- gdb.PendingFrame.name: Return the name for the frame's function,
or None.
- gdb.PendingFrame.pc: Return the $pc register value for this
frame.
- gdb.PendingFrame.language: Return a string containing the
language for this frame, or None.
- gdb.PendingFrame.find_sal: Return a gdb.Symtab_and_line object
for the current location within the pending frame, or None.
- gdb.PendingFrame.block: Return a gdb.Block for the current
pending frame, or None.
- gdb.PendingFrame.function: Return a gdb.Symbol for the current
pending frame, or None.
In every case I've just copied the implementation over from gdb.Frame
and cleaned the code slightly e.g. NULL to nullptr. Additionally each
function required a small update to reflect the PendingFrame type, but
that's pretty minor.
There are tests for all the new methods.
For more extensive testing, I added the following code to the file
gdb/python/lib/command/unwinders.py:
from gdb.unwinder import Unwinder
class TestUnwinder(Unwinder):
def __init__(self):
super().__init__("XXX_TestUnwinder_XXX")
def __call__(self,pending_frame):
lang = pending_frame.language()
try:
block = pending_frame.block()
assert isinstance(block, gdb.Block)
except RuntimeError as rte:
assert str(rte) == "Cannot locate block for frame."
function = pending_frame.function()
arch = pending_frame.architecture()
assert arch is None or isinstance(arch, gdb.Architecture)
name = pending_frame.name()
assert name is None or isinstance(name, str)
valid = pending_frame.is_valid()
pc = pending_frame.pc()
sal = pending_frame.find_sal()
assert sal is None or isinstance(sal, gdb.Symtab_and_line)
return None
gdb.unwinder.register_unwinder(None, TestUnwinder())
This registers a global unwinder that calls each of the new
PendingFrame methods and checks the result is of an acceptable type.
The unwinder never claims any frames though, so shouldn't change how
GDB actually behaves.
I then ran the testsuite. There was only a single regression, a test
that uses 'disable unwinder' and expects a single unwinder to be
disabled -- the extra unwinder is now disabled too, which changes the
test output. So I'm reasonably confident that the new methods are not
going to crash GDB.
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Reviewed-By: Tom Tromey <tom@tromey.com>
Diffstat (limited to 'gdb/python/py-unwind.c')
-rw-r--r-- | gdb/python/py-unwind.c | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/gdb/python/py-unwind.c b/gdb/python/py-unwind.c index 12b1461..5ed3ff3 100644 --- a/gdb/python/py-unwind.c +++ b/gdb/python/py-unwind.c @@ -28,6 +28,10 @@ #include "regcache.h" #include "valprint.h" #include "user-regs.h" +#include "stack.h" +#include "charset.h" +#include "block.h" + /* Debugging of Python unwinders. */ @@ -412,6 +416,200 @@ pending_framepy_read_register (PyObject *self, PyObject *args) return result; } +/* Implement PendingFrame.is_valid(). Return True if this pending frame + object is still valid. */ + +static PyObject * +pending_framepy_is_valid (PyObject *self, PyObject *args) +{ + pending_frame_object *pending_frame = (pending_frame_object *) self; + + if (pending_frame->frame_info == nullptr) + Py_RETURN_FALSE; + + Py_RETURN_TRUE; +} + +/* Implement PendingFrame.name(). Return a string that is the name of the + function for this frame, or None if the name can't be found. */ + +static PyObject * +pending_framepy_name (PyObject *self, PyObject *args) +{ + pending_frame_object *pending_frame = (pending_frame_object *) self; + + PENDING_FRAMEPY_REQUIRE_VALID (pending_frame); + + gdb::unique_xmalloc_ptr<char> name; + + try + { + enum language lang; + frame_info_ptr frame = pending_frame->frame_info; + + name = find_frame_funname (frame, &lang, nullptr); + } + catch (const gdb_exception &except) + { + GDB_PY_HANDLE_EXCEPTION (except); + } + + if (name != nullptr) + return PyUnicode_Decode (name.get (), strlen (name.get ()), + host_charset (), nullptr); + + Py_RETURN_NONE; +} + +/* Implement gdb.PendingFrame.pc(). Returns an integer containing the + frame's current $pc value. */ + +static PyObject * +pending_framepy_pc (PyObject *self, PyObject *args) +{ + pending_frame_object *pending_frame = (pending_frame_object *) self; + + PENDING_FRAMEPY_REQUIRE_VALID (pending_frame); + + CORE_ADDR pc = 0; + + try + { + pc = get_frame_pc (pending_frame->frame_info); + } + catch (const gdb_exception &except) + { + GDB_PY_HANDLE_EXCEPTION (except); + } + + return gdb_py_object_from_ulongest (pc).release (); +} + +/* Implement gdb.PendingFrame.language(). Return the name of the language + for this frame. */ + +static PyObject * +pending_framepy_language (PyObject *self, PyObject *args) +{ + pending_frame_object *pending_frame = (pending_frame_object *) self; + + PENDING_FRAMEPY_REQUIRE_VALID (pending_frame); + + try + { + frame_info_ptr fi = pending_frame->frame_info; + + enum language lang = get_frame_language (fi); + const language_defn *lang_def = language_def (lang); + + return host_string_to_python_string (lang_def->name ()).release (); + } + catch (const gdb_exception &except) + { + GDB_PY_HANDLE_EXCEPTION (except); + } + + Py_RETURN_NONE; +} + +/* Implement PendingFrame.find_sal(). Return the PendingFrame's symtab and + line. */ + +static PyObject * +pending_framepy_find_sal (PyObject *self, PyObject *args) +{ + pending_frame_object *pending_frame = (pending_frame_object *) self; + + PENDING_FRAMEPY_REQUIRE_VALID (pending_frame); + + PyObject *sal_obj = nullptr; + + try + { + frame_info_ptr frame = pending_frame->frame_info; + + symtab_and_line sal = find_frame_sal (frame); + sal_obj = symtab_and_line_to_sal_object (sal); + } + catch (const gdb_exception &except) + { + GDB_PY_HANDLE_EXCEPTION (except); + } + + return sal_obj; +} + +/* Implement PendingFrame.block(). Return a gdb.Block for the pending + frame's code, or raise RuntimeError if the block can't be found. */ + +static PyObject * +pending_framepy_block (PyObject *self, PyObject *args) +{ + pending_frame_object *pending_frame = (pending_frame_object *) self; + + PENDING_FRAMEPY_REQUIRE_VALID (pending_frame); + + frame_info_ptr frame = pending_frame->frame_info; + const struct block *block = nullptr, *fn_block; + + try + { + block = get_frame_block (frame, nullptr); + } + catch (const gdb_exception &except) + { + GDB_PY_HANDLE_EXCEPTION (except); + } + + for (fn_block = block; + fn_block != nullptr && fn_block->function () == nullptr; + fn_block = fn_block->superblock ()) + ; + + if (block == nullptr + || fn_block == nullptr + || fn_block->function () == nullptr) + { + PyErr_SetString (PyExc_RuntimeError, + _("Cannot locate block for frame.")); + return nullptr; + } + + return block_to_block_object (block, fn_block->function ()->objfile ()); +} + +/* Implement gdb.PendingFrame.function(). Return a gdb.Symbol + representing the function of this frame, or None if no suitable symbol + can be found. */ + +static PyObject * +pending_framepy_function (PyObject *self, PyObject *args) +{ + pending_frame_object *pending_frame = (pending_frame_object *) self; + + PENDING_FRAMEPY_REQUIRE_VALID (pending_frame); + + struct symbol *sym = nullptr; + + try + { + enum language funlang; + frame_info_ptr frame = pending_frame->frame_info; + + gdb::unique_xmalloc_ptr<char> funname + = find_frame_funname (frame, &funlang, &sym); + } + catch (const gdb_exception &except) + { + GDB_PY_HANDLE_EXCEPTION (except); + } + + if (sym != nullptr) + return symbol_to_symbol_object (sym); + + Py_RETURN_NONE; +} + /* Implementation of PendingFrame.create_unwind_info (self, frameId) -> UnwindInfo. */ @@ -737,6 +935,29 @@ static PyMethodDef pending_frame_object_methods[] = pending_framepy_architecture, METH_NOARGS, "architecture () -> gdb.Architecture\n" "The architecture for this PendingFrame." }, + { "name", + pending_framepy_name, METH_NOARGS, + "name() -> String.\n\ +Return the function name of the frame, or None if it can't be determined." }, + { "is_valid", + pending_framepy_is_valid, METH_NOARGS, + "is_valid () -> Boolean.\n\ +Return true if this PendingFrame is valid, false if not." }, + { "pc", + pending_framepy_pc, METH_NOARGS, + "pc () -> Long.\n\ +Return the frame's resume address." }, + { "language", pending_framepy_language, METH_NOARGS, + "The language of this frame." }, + { "find_sal", pending_framepy_find_sal, METH_NOARGS, + "find_sal () -> gdb.Symtab_and_line.\n\ +Return the frame's symtab and line." }, + { "block", pending_framepy_block, METH_NOARGS, + "block () -> gdb.Block.\n\ +Return the frame's code block." }, + { "function", pending_framepy_function, METH_NOARGS, + "function () -> gdb.Symbol.\n\ +Returns the symbol for the function corresponding to this frame." }, { "level", pending_framepy_level, METH_NOARGS, "The stack level of this frame." }, {NULL} /* Sentinel */ |