aboutsummaryrefslogtreecommitdiff
path: root/gdb/testsuite/gdb.python/py-unwind.py
diff options
context:
space:
mode:
authorAndrew Burgess <aburgess@redhat.com>2023-03-08 16:11:45 +0000
committerAndrew Burgess <aburgess@redhat.com>2023-03-30 10:25:46 +0100
commit86b35b7116a325ae0179e5b74692e4e7a1e8b2d7 (patch)
tree0ce8f9a3a3415d18be9780df12c53f97e5d3dd8e /gdb/testsuite/gdb.python/py-unwind.py
parent44d9b0a174b08f283001f01aaf84102ba0a2726a (diff)
downloadbinutils-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/testsuite/gdb.python/py-unwind.py')
-rw-r--r--gdb/testsuite/gdb.python/py-unwind.py104
1 files changed, 104 insertions, 0 deletions
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")