+2021-06-21 Andrew Burgess <andrew.burgess@embecosm.com>
+
+ * NEWS: Mention the two new methods.
+ * python/py-frame.c (frapy_level): New function.
+ (frame_object_methods): Register 'level' method.
+ * python/py-unwind.c (pending_framepy_level): New function.
+ (pending_frame_object_methods): Register 'level' method.
+
2021-06-21 Andrew Burgess <andrew.burgess@embecosm.com>
* python/py-inferior.c (infpy_get_connection_num): Call
gives the connection number as seen in 'info connections' and
'info inferiors'.
+ ** New method gdb.Frame.level() which returns the stack level of the
+ frame object.
+
+ ** New method gdb.PendingFrame.level() which returns the stack level
+ of the frame object.
+
*** Changes in GDB 10
* There are new feature names for ARC targets: "org.gnu.gdb.arc.core"
+2021-06-21 Andrew Burgess <andrew.burgess@embecosm.com>
+
+ * python.texi (Unwinding Frames in Python): Mention
+ PendingFrame.level.
+ (Frames In Python): Mention Frame.level.
+
2021-06-16 Felix Willgerodt <felix.willgerodt@intel.com>
* gdb.texinfo (Process Record and Replay): Stop mentioning lines
the particular frame being unwound.
@end defun
+@defun PendingFrame.level ()
+Return an integer, the stack frame level for this frame.
+@xref{Frames, ,Stack Frames}.
+@end defun
+
@subheading Unwinder Output: UnwindInfo
Use @code{PendingFrame.create_unwind_info} method described above to
Stack}.
@end defun
+@defun Frame.level ()
+Return an integer, the stack frame level for this frame. @xref{Frames, ,Stack Frames}.
+@end defun
+
@node Blocks In Python
@subsubsection Accessing blocks from Python
Py_RETURN_NONE;
}
+/* The stack frame level for this frame. */
+
+static PyObject *
+frapy_level (PyObject *self, PyObject *args)
+{
+ struct frame_info *fi;
+
+ try
+ {
+ FRAPY_REQUIRE_VALID (self, fi);
+
+ return gdb_py_object_from_longest (frame_relative_level (fi)).release ();
+ }
+ catch (const gdb_exception &except)
+ {
+ GDB_PY_HANDLE_EXCEPTION (except);
+ }
+
+ Py_RETURN_NONE;
+}
+
/* Implementation of gdb.newest_frame () -> gdb.Frame.
Returns the newest frame object. */
Return the value of the variable in this frame." },
{ "select", frapy_select, METH_NOARGS,
"Select this frame as the user's current frame." },
+ { "level", frapy_level, METH_NOARGS,
+ "The stack level of this frame." },
{NULL} /* Sentinel */
};
return gdbarch_to_arch_object (pending_frame->gdbarch);
}
+/* Implementation of PendingFrame.level (self) -> Integer. */
+
+static PyObject *
+pending_framepy_level (PyObject *self, PyObject *args)
+{
+ pending_frame_object *pending_frame = (pending_frame_object *) self;
+
+ if (pending_frame->frame_info == NULL)
+ {
+ PyErr_SetString (PyExc_ValueError,
+ "Attempting to read stack level from stale PendingFrame");
+ return NULL;
+ }
+ int level = frame_relative_level (pending_frame->frame_info);
+ return gdb_py_object_from_longest (level).release ();
+}
+
/* frame_unwind.this_id method. */
static void
pending_framepy_architecture, METH_NOARGS,
"architecture () -> gdb.Architecture\n"
"The architecture for this PendingFrame." },
+ { "level", pending_framepy_level, METH_NOARGS,
+ "The stack level of this frame." },
{NULL} /* Sentinel */
};
+2021-06-21 Andrew Burgess <andrew.burgess@embecosm.com>
+
+ * gdb.python/py-frame.exp: Add Frame.level tests.
+ * gdb.python/py-pending-frame-level.c: New file.
+ * gdb.python/py-pending-frame-level.exp: New file.
+ * gdb.python/py-pending-frame-level.py: New file.
+
2021-06-21 Andrew Burgess <andrew.burgess@embecosm.com>
* gdb.python/py-unwind-user-regs.c: New file.
gdb_py_test_silent_cmd "python f1 = gdb.selected_frame ()" "get second frame" 0
gdb_py_test_silent_cmd "python f0 = f1.newer ()" "get first frame" 0
+gdb_py_test_silent_cmd "python f2 = f1.older ()" "get last frame" 0
+
+# Check the Frame.level method.
+gdb_test "python print ('bframe.level = %d' % bframe.level ())" \
+ "bframe\\.level = 0"
+gdb_test "python print ('f0.level = %d' % f0.level ())" \
+ "f0\\.level = 0"
+gdb_test "python print ('f1.level = %d' % f1.level ())" \
+ "f1\\.level = 1"
+gdb_test "python print ('f2.level = %d' % f2.level ())" \
+ "f2\\.level = 2"
gdb_test "python print (f1 == gdb.newest_frame())" False \
"selected frame -vs- newest frame"
--- /dev/null
+/* This test program is part of GDB, the GNU debugger.
+
+ Copyright 2021 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/>. */
+
+volatile int global_var;
+
+void __attribute__ ((noinline))
+f0 (void)
+{
+ ++global_var; /* Break here. */
+}
+
+void __attribute__ ((noinline))
+f1 (void)
+{
+ f0 ();
+}
+
+void __attribute__ ((noinline))
+f2 (void)
+{
+ f1 ();
+}
+
+void __attribute__ ((noinline))
+f3 (void)
+{
+ f2 ();
+}
+
+int
+main (void)
+{
+ f3 ();
+ return 0;
+}
--- /dev/null
+# Copyright (C) 2021 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/>.
+
+# Test gdb.PendingFrame.level method.
+
+load_lib gdb-python.exp
+
+standard_testfile
+
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } {
+ return -1
+}
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+if ![runto_main] then {
+ fail "can't run to main"
+ return 0
+}
+
+set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
+
+gdb_breakpoint [gdb_get_line_number "Break here"]
+gdb_continue_to_breakpoint "stop at test breakpoint"
+
+# An initial look at the stack to ensure it is correct.
+gdb_test_sequence "bt" "Initial backtrace" {
+ "\\r\\n#0 \[^\r\n\]* f0 \\(\\) at "
+ "\\r\\n#1 \[^\r\n\]* f1 \\(\\) at "
+ "\\r\\n#2 \[^\r\n\]* f2 \\(\\) at "
+ "\\r\\n#3 \[^\r\n\]* f3 \\(\\) at "
+ "\\r\\n#4 \[^\r\n\]* main \\(\\) at "
+}
+
+# Load the script containing the unwinder.
+gdb_test_no_output "source ${pyfile}"\
+ "import python scripts"
+
+# Now look at the stack again, we should see output from the Python
+# unwinder mixed in.
+gdb_test_sequence "bt" "Backtrace with extra Python output" {
+ "Func f0, Level 0"
+ "Func f1, Level 1"
+ "\\r\\n#0 \[^\r\n\]* f0 \\(\\) at "
+ "\\r\\n#1 \[^\r\n\]* f1 \\(\\) at "
+ "Func f2, Level 2"
+ "\\r\\n#2 \[^\r\n\]* f2 \\(\\) at "
+ "Func f3, Level 3"
+ "\\r\\n#3 \[^\r\n\]* f3 \\(\\) at "
+ "Func main, Level 4"
+ "\\r\\n#4 \[^\r\n\]* main \\(\\) at "
+}
--- /dev/null
+# Copyright (C) 2021 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/>.
+
+import gdb
+from gdb.unwinder import Unwinder
+
+
+class FrameId(object):
+ def __init__(self, sp, pc):
+ self._sp = sp
+ self._pc = pc
+
+ @property
+ def sp(self):
+ return self._sp
+
+ @property
+ def pc(self):
+ return self._pc
+
+
+class TestUnwinder(Unwinder):
+ def __init__(self):
+ Unwinder.__init__(self, "show level")
+
+ def __call__(self, pending_frame):
+ pc_desc = pending_frame.architecture().registers().find("pc")
+ pc = pending_frame.read_register(pc_desc)
+
+ block = gdb.block_for_pc(int(pc))
+ if block is None:
+ return None
+ func = block.function
+ if func is None:
+ return None
+
+ print("Func %s, Level %d" % (str(func), pending_frame.level()))
+
+ # This unwinder never claims any frames.
+ return None
+
+
+gdb.unwinder.register_unwinder(None, TestUnwinder(), True)