aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/NEWS20
-rw-r--r--gdb/doc/python.texi40
-rw-r--r--gdb/python/py-unwind.c221
-rw-r--r--gdb/testsuite/gdb.python/py-unwind.exp33
-rw-r--r--gdb/testsuite/gdb.python/py-unwind.py104
5 files changed, 417 insertions, 1 deletions
diff --git a/gdb/NEWS b/gdb/NEWS
index a3f7e1c..d5316ff 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -120,6 +120,26 @@ show always-read-ctf
without the particular unwinder, depending on how 'enabled' was
changed.
+ ** New methods added to the gdb.PendingFrame class. These methods
+ have the same behaviour as the corresponding methods on
+ gdb.Frame. The new methods are:
+
+ - gdb.PendingFrame.name: Return the name for the frame's
+ function, or None.
+ - gdb.PendingFrame.is_valid: Return True if the pending frame
+ object is valid.
+ - 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.
+
*** Changes in GDB 13
* MI version 1 is deprecated, and will be removed in GDB 14.
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 8dcb3da..80697f9 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -2830,6 +2830,46 @@ Return an integer, the stack frame level for this frame.
@xref{Frames, ,Stack Frames}.
@end defun
+@defun PendingFrame.name ()
+Returns the function name of this pending frame, or @code{None} if it
+can't be obtained.
+@end defun
+
+@defun PendingFrame.is_valid ()
+Returns true if the @code{gdb.PendingFrame} object is valid, false if
+not. A pending frame object becomes invalid when the call to the
+unwinder, for which the pending frame was created, returns.
+
+All @code{gdb.PendingFrame} methods, except this one, will raise an
+exception if the pending frame object is invalid at the time the
+method is called.
+@end defun
+
+@defun PendingFrame.pc ()
+Returns the pending frame's resume address.
+@end defun
+
+@defun PendingFrame.block ()
+Return the pending frame's code block (@pxref{Blocks In Python}). If
+the frame does not have a block -- for example, if there is no
+debugging information for the code in question -- then this will raise
+a @code{RuntimeError} exception.
+@end defun
+
+@defun PendingFrame.function ()
+Return the symbol for the function corresponding to this pending frame.
+@xref{Symbols In Python}.
+@end defun
+
+@defun PendingFrame.find_sal ()
+Return the pending frame's symtab and line object (@pxref{Symbol
+Tables In Python}).
+@end defun
+
+@defun PendingFrame.language ()
+Return the language of this frame, as a string, or None.
+@end defun
+
@subheading Unwinder Output: UnwindInfo
Use @code{PendingFrame.create_unwind_info} method described above to
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 */
diff --git a/gdb/testsuite/gdb.python/py-unwind.exp b/gdb/testsuite/gdb.python/py-unwind.exp
index 3e214ee..f670da5 100644
--- a/gdb/testsuite/gdb.python/py-unwind.exp
+++ b/gdb/testsuite/gdb.python/py-unwind.exp
@@ -149,15 +149,46 @@ gdb_test_no_output "python obj = simple_unwinder(\"simple\")"
gdb_test_no_output "python gdb.unwinder.register_unwinder(None, obj)"
check_for_broken_backtrace "backtrace to capture a PendingFrame object"
+# Check the captured PendingFrame is not valid.
+gdb_test "python print(captured_pending_frame.is_valid())" "False"
+
# Call methods on the captured gdb.PendingFrame and check we see the
# expected error.
gdb_test_no_output "python pf = captured_pending_frame"
foreach cmd {"pf.read_register(\"pc\")" \
"pf.create_unwind_info(None)" \
"pf.architecture()" \
- "pf.level()"} {
+ "pf.level()" \
+ "pf.name()" \
+ "pf.pc()" \
+ "pf.language()" \
+ "pf.find_sal()" \
+ "pf.block()" \
+ "pf.function()" } {
gdb_test "python $cmd" \
[multi_line \
"ValueError: gdb\\.PendingFrame is invalid\\." \
"Error while executing Python code\\."]
}
+
+# Turn on the useful unwinder so we have the full backtrace again, and
+# disable the simple unwinder -- because we can!
+gdb_test "enable unwinder global \"test unwinder\"" \
+ "1 unwinder enabled" \
+ "re-enable 'test unwinder' so we can check PendingFrame methods"
+gdb_test "disable unwinder global \"simple\"" \
+ "1 unwinder disabled"
+check_for_fixed_backtrace \
+ "check backtrace before testing PendingFrame methods"
+
+# Gather information about every frame.
+gdb_test_no_output "python capture_all_frame_information()"
+gdb_test_no_output "python gdb.newest_frame().select()"
+gdb_test_no_output "python pspace = gdb.selected_inferior().progspace"
+gdb_test_no_output "python obj = validating_unwinder()"
+gdb_test_no_output "python gdb.unwinder.register_unwinder(pspace, obj)"
+
+check_for_fixed_backtrace \
+ "check backtrace to validate all information"
+
+gdb_test_no_output "python check_all_frame_information_matched()"
diff --git a/gdb/testsuite/gdb.python/py-unwind.py b/gdb/testsuite/gdb.python/py-unwind.py
index b30e843..8e3c1f3 100644
--- a/gdb/testsuite/gdb.python/py-unwind.py
+++ b/gdb/testsuite/gdb.python/py-unwind.py
@@ -144,9 +144,113 @@ class simple_unwinder(Unwinder):
def __call__(self, pending_frame):
global captured_pending_frame
+ assert pending_frame.is_valid()
+
if captured_pending_frame is None:
captured_pending_frame = pending_frame
return None
+# Return a dictionary of information about FRAME.
+def capture_frame_information(frame):
+ name = frame.name()
+ level = frame.level()
+ language = frame.language()
+ function = frame.function()
+ architecture = frame.architecture()
+ pc = frame.pc()
+ sal = frame.find_sal()
+ try:
+ block = frame.block()
+ assert isinstance(block, gdb.Block)
+ except RuntimeError as rte:
+ assert str(rte) == "Cannot locate block for frame."
+ block = "RuntimeError: " + str(rte)
+
+ return {
+ "name": name,
+ "level": level,
+ "language": language,
+ "function": function,
+ "architecture": architecture,
+ "pc": pc,
+ "sal": sal,
+ "block": block,
+ }
+
+
+# List of information about each frame. The index into this list is
+# the frame level. This is populated by
+# capture_all_frame_information.
+all_frame_information = []
+
+# Fill in the global ALL_FRAME_INFORMATION list.
+def capture_all_frame_information():
+ global all_frame_information
+
+ all_frame_information = []
+
+ gdb.newest_frame().select()
+ frame = gdb.selected_frame()
+ count = 0
+
+ while frame is not None:
+ frame.select()
+ info = capture_frame_information(frame)
+ level = info["level"]
+ info["matched"] = False
+
+ while len(all_frame_information) <= level:
+ all_frame_information.append(None)
+
+ assert all_frame_information[level] is None
+ all_frame_information[level] = info
+
+ if frame.name == "main" or count > 10:
+ break
+
+ count += 1
+ frame = frame.older()
+
+
+# Assert that every entry in the global ALL_FRAME_INFORMATION list was
+# matched by the validating_unwinder.
+def check_all_frame_information_matched():
+ global all_frame_information
+ for entry in all_frame_information:
+ assert entry["matched"]
+
+
+# An unwinder that doesn't match any frames. What it does do is
+# lookup information from the PendingFrame object and compare it
+# against information stored in the global ALL_FRAME_INFORMATION list.
+class validating_unwinder(Unwinder):
+ def __init__(self):
+ super().__init__("validating_unwinder")
+
+ def __call__(self, pending_frame):
+ info = capture_frame_information(pending_frame)
+ level = info["level"]
+
+ global all_frame_information
+ old_info = all_frame_information[level]
+
+ assert old_info is not None
+ assert not old_info["matched"]
+
+ for key, value in info.items():
+ assert key in old_info, f"{key} not in old_info"
+ assert type(value) == type(old_info[key])
+ if isinstance(value, gdb.Block):
+ assert value.start == old_info[key].start
+ assert value.end == old_info[key].end
+ assert value.is_static == old_info[key].is_static
+ assert value.is_global == old_info[key].is_global
+ else:
+ assert str(value) == str(old_info[key])
+
+ old_info["matched"] = True
+ return None
+
+
print("Python script imported")