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
# 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
_all_frames = []
global _iter_map
_iter_map = {}
+ global thread_ids
+ thread_ids = {}
# Clear the frame ID map whenever the inferior runs.
@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]
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):
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)
# 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
@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
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]),
)
# 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: