[3/6] gdb/python: add gdb.Frame.__repr__() method

Message ID bc2b34fe07565f345511807f5c57fe36bead81aa.1704455158.git.aburgess@redhat.com
State New
Headers
Series Python __repr__() methods and new __dict__ attributes |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gdb_check--master-arm fail Testing failed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 fail Testing failed

Commit Message

Andrew Burgess Jan. 5, 2024, 11:48 a.m. UTC
  Add a gdb.Frame.__repr__() method.  Before this patch we would see
output like this:

  (gdb) pi
  >>> gdb.selected_frame()
  <gdb.Frame object at 0x7fa8cc2df270>

After this patch, we now see:

  (gdb) pi
  >>> gdb.selected_frame()
  <gdb.Frame level=0 frame-id={stack=0x7ffff7da0ed0,code=0x000000000040115d,!special}>

More verbose, but, I hope, more useful.

If the gdb.Frame becomes invalid, then we will see:

  (gdb) pi
  >>> invalid_frame_variable
  <gdb.Frame (invalid)>

which is inline with how other invalid objects are displayed.
---
 gdb/python/py-frame.c                 | 19 ++++++++++++++++++-
 gdb/testsuite/gdb.python/py-frame.exp |  8 ++++++++
 2 files changed, 26 insertions(+), 1 deletion(-)
  

Patch

diff --git a/gdb/python/py-frame.c b/gdb/python/py-frame.c
index f38bf5d8237..ee465ed64a1 100644
--- a/gdb/python/py-frame.c
+++ b/gdb/python/py-frame.c
@@ -84,6 +84,23 @@  frapy_str (PyObject *self)
   return PyUnicode_FromString (fid.to_string ().c_str ());
 }
 
+/* Implement repr() for gdb.Frame.  */
+
+static PyObject *
+frapy_repr (PyObject *self)
+{
+  frame_object *frame_obj = (frame_object *) self;
+  frame_info_ptr f_info = frame_find_by_id (frame_obj->frame_id);
+  if (f_info == nullptr)
+    return gdb_py_invalid_object_repr (self);
+
+  const frame_id &fid = frame_obj->frame_id;
+  return PyUnicode_FromFormat ("<%s level=%d frame-id=%s>",
+			       Py_TYPE (self)->tp_name,
+			       frame_relative_level (f_info),
+			       fid.to_string ().c_str ());
+}
+
 /* Implementation of gdb.Frame.is_valid (self) -> Boolean.
    Returns True if the frame corresponding to the frame_id of this
    object still exists in the inferior.  */
@@ -840,7 +857,7 @@  PyTypeObject frame_object_type = {
   0,				  /* tp_getattr */
   0,				  /* tp_setattr */
   0,				  /* tp_compare */
-  0,				  /* tp_repr */
+  frapy_repr,			  /* tp_repr */
   0,				  /* tp_as_number */
   0,				  /* tp_as_sequence */
   0,				  /* tp_as_mapping */
diff --git a/gdb/testsuite/gdb.python/py-frame.exp b/gdb/testsuite/gdb.python/py-frame.exp
index 16177c8a5f8..6162696948f 100644
--- a/gdb/testsuite/gdb.python/py-frame.exp
+++ b/gdb/testsuite/gdb.python/py-frame.exp
@@ -36,6 +36,10 @@  gdb_breakpoint [gdb_get_line_number "Block break here."]
 gdb_continue_to_breakpoint "Block break here."
 gdb_py_test_silent_cmd "python bf1 = gdb.selected_frame ()" "get frame" 0
 
+# Test Frame.__repr__() method for a valid Frame object.
+gdb_test "python print (repr(bf1))" "<gdb\\.Frame level=0 frame-id=\\{\[^\r\n\]+\\}>" \
+    "test __repr__ for gdb.Frame on a valid frame"
+
 # Test Frame.architecture() method.
 gdb_py_test_silent_cmd "python show_arch_str = gdb.execute(\"show architecture\", to_string=True)" "show arch" 0
 gdb_test "python print (bf1.architecture().name() in show_arch_str)" "True" "test Frame.architecture()"
@@ -100,6 +104,10 @@  gdb_py_test_silent_cmd "python bframe = gdb.selected_frame()" \
     "get bottommost frame" 0
 gdb_test "up" ".*" ""
 
+# Test Frame.__repr__() method for an invalid Frame object.
+gdb_test "python print (repr(bf1))" "<gdb\\.Frame \\(invalid\\)>" \
+    "test __repr__ for gdb.Frame on an invalid frame"
+
 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