[binutils-gdb] gdb/python: add __repr__ for PendingFrame and UnwindInfo

Andrew Burgess aburgess@sourceware.org
Thu Mar 30 09:26:17 GMT 2023


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=7e6af18d0c7df5d1ee6144ef0e09b32db3a347e7

commit 7e6af18d0c7df5d1ee6144ef0e09b32db3a347e7
Author: Andrew Burgess <aburgess@redhat.com>
Date:   Fri Mar 10 10:19:58 2023 +0000

    gdb/python: add __repr__ for PendingFrame and UnwindInfo
    
    Having a useful __repr__ method can make debugging Python code that
    little bit easier.  This commit adds __repr__ for gdb.PendingFrame and
    gdb.UnwindInfo classes, along with some tests.
    
    Reviewed-By: Tom Tromey <tom@tromey.com>

Diff:
---
 gdb/python/py-unwind.c                 | 67 +++++++++++++++++++++++++++++++++-
 gdb/testsuite/gdb.python/py-unwind.exp | 19 ++++++++++
 gdb/testsuite/gdb.python/py-unwind.py  | 16 +++++++-
 3 files changed, 99 insertions(+), 3 deletions(-)

diff --git a/gdb/python/py-unwind.c b/gdb/python/py-unwind.c
index 5ed3ff37e64..1e94c243163 100644
--- a/gdb/python/py-unwind.c
+++ b/gdb/python/py-unwind.c
@@ -229,6 +229,38 @@ unwind_infopy_str (PyObject *self)
   return PyUnicode_FromString (stb.c_str ());
 }
 
+/* Implement UnwindInfo.__repr__().  */
+
+static PyObject *
+unwind_infopy_repr (PyObject *self)
+{
+  unwind_info_object *unwind_info = (unwind_info_object *) self;
+  pending_frame_object *pending_frame
+    = (pending_frame_object *) (unwind_info->pending_frame);
+  frame_info_ptr frame = pending_frame->frame_info;
+
+  if (frame == nullptr)
+    return PyUnicode_FromFormat ("<%s for an invalid frame>",
+				 Py_TYPE (self)->tp_name);
+
+  std::string saved_reg_names;
+  struct gdbarch *gdbarch = pending_frame->gdbarch;
+
+  for (const saved_reg &reg : *unwind_info->saved_regs)
+    {
+      const char *name = gdbarch_register_name (gdbarch, reg.number);
+      if (saved_reg_names.empty ())
+	saved_reg_names = name;
+      else
+	saved_reg_names = (saved_reg_names + ", ") + name;
+    }
+
+  return PyUnicode_FromFormat ("<%s frame #%d, saved_regs=(%s)>",
+			       Py_TYPE (self)->tp_name,
+			       frame_relative_level (frame),
+			       saved_reg_names.c_str ());
+}
+
 /* Create UnwindInfo instance for given PendingFrame and frame ID.
    Sets Python error and returns NULL on error.
 
@@ -372,6 +404,37 @@ pending_framepy_str (PyObject *self)
   return PyUnicode_FromFormat ("SP=%s,PC=%s", sp_str, pc_str);
 }
 
+/* Implement PendingFrame.__repr__().  */
+
+static PyObject *
+pending_framepy_repr (PyObject *self)
+{
+  pending_frame_object *pending_frame = (pending_frame_object *) self;
+  frame_info_ptr frame = pending_frame->frame_info;
+
+  if (frame == nullptr)
+    return PyUnicode_FromFormat ("<%s (invalid)>", Py_TYPE (self)->tp_name);
+
+  const char *sp_str = nullptr;
+  const char *pc_str = nullptr;
+
+  try
+    {
+      sp_str = core_addr_to_string_nz (get_frame_sp (frame));
+      pc_str = core_addr_to_string_nz (get_frame_pc (frame));
+    }
+  catch (const gdb_exception &except)
+    {
+      GDB_PY_HANDLE_EXCEPTION (except);
+    }
+
+  return PyUnicode_FromFormat ("<%s level=%d, sp=%s, pc=%s>",
+			       Py_TYPE (self)->tp_name,
+			       frame_relative_level (frame),
+			       sp_str,
+			       pc_str);
+}
+
 /* Implementation of gdb.PendingFrame.read_register (self, reg) -> gdb.Value.
    Returns the value of register REG as gdb.Value instance.  */
 
@@ -974,7 +1037,7 @@ PyTypeObject pending_frame_object_type =
   0,                              /* tp_getattr */
   0,                              /* tp_setattr */
   0,                              /* tp_compare */
-  0,                              /* tp_repr */
+  pending_framepy_repr,           /* tp_repr */
   0,                              /* tp_as_number */
   0,                              /* tp_as_sequence */
   0,                              /* tp_as_mapping */
@@ -1024,7 +1087,7 @@ PyTypeObject unwind_info_object_type =
   0,                              /* tp_getattr */
   0,                              /* tp_setattr */
   0,                              /* tp_compare */
-  0,                              /* tp_repr */
+  unwind_infopy_repr,             /* tp_repr */
   0,                              /* tp_as_number */
   0,                              /* tp_as_sequence */
   0,                              /* tp_as_mapping */
diff --git a/gdb/testsuite/gdb.python/py-unwind.exp b/gdb/testsuite/gdb.python/py-unwind.exp
index f670da5a7cd..7e6ac9a8623 100644
--- a/gdb/testsuite/gdb.python/py-unwind.exp
+++ b/gdb/testsuite/gdb.python/py-unwind.exp
@@ -152,6 +152,25 @@ 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"
 
+# Check the __repr__ of an invalid PendingFrame.
+gdb_test "python print(repr(captured_pending_frame))" \
+    "<gdb.PendingFrame \\(invalid\\)>"
+
+# Check the __repr__ of an UnwindInfo for an invalid PendingFrame.
+gdb_test "python print(captured_unwind_info)"
+gdb_test "python print(repr(captured_unwind_info))" \
+    "<gdb.UnwindInfo for an invalid frame>"
+
+# Check the repr of a PendingFrame that was copied (as a string) at a
+# time the PendingFrame was valid.
+gdb_test "python print(captured_pending_frame_repr)" \
+    "<gdb.PendingFrame level=0, sp=$hex, pc=$hex>"
+
+# Check the repr of an UnwindInfo that was copied (as a string) at a
+# time the UnwindInfo was valid.
+gdb_test "python print(captured_unwind_info_repr)" \
+    "<gdb.UnwindInfo frame #0, saved_regs=\\(rip, rbp, rsp\\)>"
+
 # Call methods on the captured gdb.PendingFrame and check we see the
 # expected error.
 gdb_test_no_output "python pf = captured_pending_frame"
diff --git a/gdb/testsuite/gdb.python/py-unwind.py b/gdb/testsuite/gdb.python/py-unwind.py
index 8e3c1f398bc..f8f04b7f514 100644
--- a/gdb/testsuite/gdb.python/py-unwind.py
+++ b/gdb/testsuite/gdb.python/py-unwind.py
@@ -133,8 +133,11 @@ class TestUnwinder(Unwinder):
 global_test_unwinder = TestUnwinder()
 gdb.unwinder.register_unwinder(None, global_test_unwinder, True)
 
-# This is filled in by the simple_unwinder class.
+# These are filled in by the simple_unwinder class.
 captured_pending_frame = None
+captured_pending_frame_repr = None
+captured_unwind_info = None
+captured_unwind_info_repr = None
 
 
 class simple_unwinder(Unwinder):
@@ -143,11 +146,22 @@ class simple_unwinder(Unwinder):
 
     def __call__(self, pending_frame):
         global captured_pending_frame
+        global captured_pending_frame_repr
+        global captured_unwind_info
+        global captured_unwind_info_repr
 
         assert pending_frame.is_valid()
 
         if captured_pending_frame is None:
             captured_pending_frame = pending_frame
+            captured_pending_frame_repr = repr(pending_frame)
+            fid = FrameId(gdb.Value(0x123), gdb.Value(0x456))
+            uw = pending_frame.create_unwind_info(fid)
+            uw.add_saved_register("rip", gdb.Value(0x123))
+            uw.add_saved_register("rbp", gdb.Value(0x456))
+            uw.add_saved_register("rsp", gdb.Value(0x789))
+            captured_unwind_info = uw
+            captured_unwind_info_repr = repr(uw)
         return None


More information about the Gdb-cvs mailing list