mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-11-16 12:34:43 +00:00
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:
committed by
Tom Tromey
parent
ef1f1b7ac6
commit
83c1269833
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user