gdb-dap: fix gdb.error: Frame is invalid.

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>
This commit is contained in:
Oleg Tolmatcev
2024-08-26 23:11:36 +02:00
committed by Tom Tromey
parent ef1f1b7ac6
commit 83c1269833
2 changed files with 34 additions and 18 deletions

View 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):

View 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: