]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gdb-dap: fix gdb.error: Frame is invalid.
authorOleg Tolmatcev <oleg.tolmatcev@gmail.com>
Mon, 26 Aug 2024 21:11:36 +0000 (23:11 +0200)
committerTom Tromey <tromey@adacore.com>
Fri, 13 Dec 2024 16:46:15 +0000 (09:46 -0700)
When you try to use a frame on one thread and it was created on
another you get an error. I fixed it by creating a map from a frame ID
to a thread ID. When a frame is created it is added to the map. When
you try to find a frame for an id it checks if it is on the correct
thread and if not switches to that thread. I had to store the frame id
instead of the frame itself in a "_ScopeReference".

Signed-off-by: Oleg Tolmatcev <oleg.tolmatcev@gmail.com>
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32133
Approved-By: Tom Tromey <tom@tromey.com>
gdb/python/lib/gdb/dap/frames.py
gdb/python/lib/gdb/dap/scopes.py

index 07a4e3ea793d22e26eb19f008e14589fd7602e47..12113961386313aa595f2a50749a0f1d0b120d58 100644 (file)
@@ -19,6 +19,7 @@ import gdb
 from gdb.frames import frame_iterator
 
 from .startup import in_gdb_thread
+from .state import set_thread
 
 # A list of all the frames we've reported.  A frame's index in the
 # list is its ID.  We don't use a hash here because frames are not
@@ -29,6 +30,9 @@ _all_frames = []
 # Map from a global thread ID to a memoizing frame iterator.
 _iter_map = {}
 
+# Map from a global frame ID to a thread ID.
+thread_ids: dict[int, int] = {}
+
 
 # Clear all the frame IDs.
 @in_gdb_thread
@@ -37,6 +41,8 @@ def _clear_frame_ids(evt):
     _all_frames = []
     global _iter_map
     _iter_map = {}
+    global thread_ids
+    thread_ids = {}
 
 
 # Clear the frame ID map whenever the inferior runs.
@@ -46,6 +52,11 @@ gdb.events.cont.connect(_clear_frame_ids)
 @in_gdb_thread
 def frame_for_id(id):
     """Given a frame identifier ID, return the corresponding frame."""
+    global thread_ids
+    if id in thread_ids:
+        thread_id = thread_ids[id]
+        if thread_id != gdb.selected_thread().global_num:
+            set_thread(thread_id)
     global _all_frames
     return _all_frames[id]
 
@@ -94,6 +105,8 @@ def _frame_id_generator():
         global _all_frames
         num = len(_all_frames)
         _all_frames.append(frame)
+        global thread_ids
+        thread_ids[num] = gdb.selected_thread().global_num
         return num
 
     def yield_frames(iterator, for_elided):
index fb90f647a0f621f2fc3c7131c893ab4892493a29..2fb3f77f1e3293fa144812a2200e915db8eb5d4a 100644 (file)
@@ -76,13 +76,10 @@ def symbol_value(sym, frame):
 
 
 class _ScopeReference(BaseReference):
-    def __init__(self, name, hint, frame, var_list):
+    def __init__(self, name, hint, frameId: int, var_list):
         super().__init__(name)
         self.hint = hint
-        self.frame = frame
-        self.inf_frame = frame.inferior_frame()
-        self.func = frame.function()
-        self.line = frame.line()
+        self.frameId = frameId
         # VAR_LIST might be any kind of iterator, but it's convenient
         # here if it is just a collection.
         self.var_list = tuple(var_list)
@@ -93,9 +90,10 @@ class _ScopeReference(BaseReference):
         # How would we know?
         result["expensive"] = False
         result["namedVariables"] = self.child_count()
-        if self.line is not None:
-            result["line"] = self.line
-        filename = self.frame.filename()
+        frame = frame_for_id(self.frameId)
+        if frame.line() is not None:
+            result["line"] = frame.line()
+        filename = frame.filename()
         if filename is not None:
             result["source"] = make_source(filename)
         return result
@@ -108,14 +106,14 @@ class _ScopeReference(BaseReference):
 
     @in_gdb_thread
     def fetch_one_child(self, idx):
-        return symbol_value(self.var_list[idx], self.frame)
+        return symbol_value(self.var_list[idx], frame_for_id(self.frameId))
 
 
 # A _ScopeReference that wraps the 'finish' value.  Note that this
 # object is only created if such a value actually exists.
 class _FinishScopeReference(_ScopeReference):
-    def __init__(self, frame):
-        super().__init__("Return", "returnValue", frame, ())
+    def __init__(self, frameId):
+        super().__init__("Return", "returnValue", frameId, ())
 
     def child_count(self):
         return 1
@@ -127,16 +125,21 @@ class _FinishScopeReference(_ScopeReference):
 
 
 class _RegisterReference(_ScopeReference):
-    def __init__(self, name, frame):
+    def __init__(self, name, frameId):
         super().__init__(
-            name, "registers", frame, frame.inferior_frame().architecture().registers()
+            name,
+            "registers",
+            frameId,
+            frame_for_id(frameId).inferior_frame().architecture().registers(),
         )
 
     @in_gdb_thread
     def fetch_one_child(self, idx):
         return (
             self.var_list[idx].name,
-            self.inf_frame.read_register(self.var_list[idx]),
+            frame_for_id(self.frameId)
+            .inferior_frame()
+            .read_register(self.var_list[idx]),
         )
 
 
@@ -153,16 +156,16 @@ def scopes(*, frameId: int, **extra):
         # iterator case.
         args = tuple(frame.frame_args() or ())
         if args:
-            scopes.append(_ScopeReference("Arguments", "arguments", frame, args))
+            scopes.append(_ScopeReference("Arguments", "arguments", frameId, args))
         has_return_value = frameId == 0 and _last_return_value is not None
         # Make sure to handle the None case as well as the empty
         # iterator case.
         locs = tuple(frame.frame_locals() or ())
         if locs:
-            scopes.append(_ScopeReference("Locals", "locals", frame, locs))
-        scopes.append(_RegisterReference("Registers", frame))
+            scopes.append(_ScopeReference("Locals", "locals", frameId, locs))
+        scopes.append(_RegisterReference("Registers", frameId))
         if has_return_value:
-            scopes.append(_FinishScopeReference(frame))
+            scopes.append(_FinishScopeReference(frameId))
         frame_to_scope[frameId] = scopes
         global_scope = get_global_scope(frame)
         if global_scope is not None: