import gdb
import os
+import itertools
+from gdb.frames import frame_iterator
+from gdb.FrameIterator import FrameIterator
+from gdb.FrameDecorator import FrameDecorator
from .frames import frame_id
from .server import request, capability
from .startup import send_gdb_with_response, in_gdb_thread
from .state import set_thread
-# Helper function to safely get the name of a frame as a string.
-@in_gdb_thread
-def _frame_name(frame):
- name = frame.name()
- if name is None:
- name = "???"
- return name
-
-
-# Helper function to get a frame's SAL without an error.
-@in_gdb_thread
-def _safe_sal(frame):
- try:
- return frame.find_sal()
- except gdb.error:
- return None
-
-
# Helper function to compute a stack trace.
@in_gdb_thread
def _backtrace(thread_id, levels, startFrame):
set_thread(thread_id)
frames = []
- current_number = 0
+ if levels == 0:
+ # Zero means all remaining frames.
+ high = -1
+ else:
+ # frame_iterator uses an inclusive range, so subtract one.
+ high = startFrame + levels - 1
try:
- current_frame = gdb.newest_frame()
+ frame_iter = frame_iterator(gdb.newest_frame(), startFrame, high)
except gdb.error:
- current_frame = None
- while current_frame is not None and (levels == 0 or len(frames) < levels):
- # This condition handles the startFrame==0 case as well.
- if current_number >= startFrame:
- newframe = {
- "id": frame_id(current_frame),
- "name": _frame_name(current_frame),
- # This must always be supplied, but we will set it
- # correctly later if that is possible.
- "line": 0,
- # GDB doesn't support columns.
- "column": 0,
- "instructionPointerReference": hex(current_frame.pc()),
+ frame_iter = ()
+ for current_frame in frame_iter:
+ newframe = {
+ "id": frame_id(current_frame),
+ "name": current_frame.function(),
+ # This must always be supplied, but we will set it
+ # correctly later if that is possible.
+ "line": 0,
+ # GDB doesn't support columns.
+ "column": 0,
+ "instructionPointerReference": hex(current_frame.address()),
+ }
+ line = current_frame.line()
+ if line is not None:
+ newframe["line"] = line
+ filename = current_frame.filename()
+ if filename is not None:
+ newframe["source"] = {
+ "name": os.path.basename(filename),
+ "path": filename,
+ # We probably don't need this but it doesn't hurt
+ # to be explicit.
+ "sourceReference": 0,
}
- sal = _safe_sal(current_frame)
- if sal is not None and sal.symtab is not None:
- newframe["source"] = {
- "name": os.path.basename(sal.symtab.filename),
- "path": sal.symtab.filename,
- # We probably don't need this but it doesn't hurt
- # to be explicit.
- "sourceReference": 0,
- }
- newframe["line"] = sal.line
- frames.append(newframe)
- current_number = current_number + 1
- current_frame = current_frame.older()
+ frames.append(newframe)
# Note that we do not calculate totalFrames here. Its absence
# tells the client that it may simply ask for frames until a
# response yields fewer frames than requested.
# This is deprecated in 3.9, but required in older versions.
from typing import Optional
-from .frames import frame_for_id
+from .frames import select_frame
from .server import capability, request, client_bool_capability
from .startup import send_gdb_with_response, in_gdb_thread
from .varref import find_variable, VariableReference
def _evaluate(expr, frame_id):
global_context = True
if frame_id is not None:
- frame = frame_for_id(frame_id)
- frame.select()
+ select_frame(frame_id)
global_context = False
val = gdb.parse_and_eval(expr, global_context=global_context)
ref = EvaluateResult(val)
def _set_expression(expression, value, frame_id):
global_context = True
if frame_id is not None:
- frame = frame_for_id(frame_id)
- frame.select()
+ select_frame(frame_id)
global_context = False
lhs = gdb.parse_and_eval(expression, global_context=global_context)
rhs = gdb.parse_and_eval(value, global_context=global_context)
@in_gdb_thread
def _repl(command, frame_id):
if frame_id is not None:
- frame = frame_for_id(frame_id)
- frame.select()
+ select_frame(frame_id)
val = gdb.execute(command, from_tty=True, to_string=True)
return {
"result": val,
"""Given a frame identifier ID, return the corresponding frame."""
global _all_frames
return _all_frames[id]
+
+
+@in_gdb_thread
+def select_frame(id):
+ """Given a frame identifier ID, select the corresponding frame."""
+ frame = frame_for_id(id)
+ frame.inferior_frame().select()
from .varref import BaseReference
-# Helper function to return a frame's block without error.
-@in_gdb_thread
-def _safe_block(frame):
- try:
- return frame.block()
- except gdb.error:
- return None
-
-
-# Helper function to return two lists of variables of block, up to the
-# enclosing function. The result is of the form (ARGS, LOCALS), where
-# each element is itself a list.
-@in_gdb_thread
-def _block_vars(block):
- args = []
- locs = []
- while True:
- for var in block:
- if var.is_argument:
- args.append(var)
- elif var.is_variable or var.is_constant:
- locs.append(var)
- if block.function is not None:
- break
- block = block.superblock
- return (args, locs)
-
-
-class ScopeReference(BaseReference):
+class _ScopeReference(BaseReference):
def __init__(self, name, hint, frame, var_list):
super().__init__(name)
self.hint = hint
self.frame = frame
+ self.inf_frame = frame.inferior_frame()
self.func = frame.function()
- self.var_list = var_list
+ self.line = frame.line()
+ # 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)
def to_object(self):
result = super().to_object()
# How would we know?
result["expensive"] = False
result["namedVariables"] = len(self.var_list)
- if self.func is not None:
- result["line"] = self.func.line
+ if self.line is not None:
+ result["line"] = self.line
# FIXME construct a Source object
return result
@in_gdb_thread
def fetch_one_child(self, idx):
+ # Here SYM will conform to the SymValueWrapper interface.
sym = self.var_list[idx]
- if sym.needs_frame:
- val = sym.value(self.frame)
- else:
- val = sym.value()
- return (sym.print_name, val)
-
-
-class RegisterReference(ScopeReference):
+ name = str(sym.symbol())
+ val = sym.value()
+ if val is None:
+ # No synthetic value, so must read the symbol value
+ # ourselves.
+ val = sym.symbol().value(self.inf_frame)
+ elif not isinstance(val, gdb.Value):
+ val = gdb.Value(val)
+ return (name, val)
+
+
+class _RegisterReference(_ScopeReference):
def __init__(self, name, frame):
super().__init__(
- name, "registers", frame, list(frame.architecture().registers())
+ name, "registers", frame, frame.inferior_frame().architecture().registers()
)
@in_gdb_thread
def fetch_one_child(self, idx):
- return (self.var_list[idx].name, self.frame.read_register(self.var_list[idx]))
+ return (
+ self.var_list[idx].name,
+ self.inf_frame.read_register(self.var_list[idx]),
+ )
# Helper function to create a DAP scopes for a given frame ID.
@in_gdb_thread
def _get_scope(id):
frame = frame_for_id(id)
- block = _safe_block(frame)
scopes = []
- if block is not None:
- (args, locs) = _block_vars(block)
- if args:
- scopes.append(ScopeReference("Arguments", "arguments", frame, args))
- if locs:
- scopes.append(ScopeReference("Locals", "locals", frame, locs))
- scopes.append(RegisterReference("Registers", frame))
+ args = frame.frame_args()
+ if args:
+ scopes.append(_ScopeReference("Arguments", "arguments", frame, args))
+ locs = frame.frame_locals()
+ if locs:
+ scopes.append(_ScopeReference("Locals", "locals", frame, locs))
+ scopes.append(_RegisterReference("Registers", frame))
return [x.to_object() for x in scopes]
{
const char *inner = "inner block";
- return 0; /* BREAK */
+ /* Make sure to use 'scalar'. */
+ return scalar - 23; /* BREAK */
}
}