diff options
-rw-r--r-- | gdb/python/lib/gdb/dap/bt.py | 102 | ||||
-rw-r--r-- | gdb/python/lib/gdb/dap/evaluate.py | 72 | ||||
-rw-r--r-- | gdb/python/lib/gdb/dap/varref.py | 18 | ||||
-rw-r--r-- | gdb/testsuite/gdb.dap/hover.exp | 10 |
4 files changed, 124 insertions, 78 deletions
diff --git a/gdb/python/lib/gdb/dap/bt.py b/gdb/python/lib/gdb/dap/bt.py index 975c88f..4d1d89a 100644 --- a/gdb/python/lib/gdb/dap/bt.py +++ b/gdb/python/lib/gdb/dap/bt.py @@ -22,60 +22,66 @@ from .modules import module_id from .server import request, capability from .startup import send_gdb_with_response, in_gdb_thread from .state import set_thread +from .varref import apply_format # Helper function to compute a stack trace. @in_gdb_thread -def _backtrace(thread_id, levels, startFrame): - set_thread(thread_id) - frames = [] - 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: - frame_iter = frame_iterator(gdb.newest_frame(), startFrame, high) - except gdb.error: - frame_iter = () - for current_frame in frame_iter: - pc = current_frame.address() - 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(pc), - } - objfile = gdb.current_progspace().objfile_for_address(pc) - if objfile is not None: - newframe["moduleId"] = module_id(objfile) - 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, +def _backtrace(thread_id, levels, startFrame, value_format): + with apply_format(value_format): + set_thread(thread_id) + frames = [] + 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: + frame_iter = frame_iterator(gdb.newest_frame(), startFrame, high) + except gdb.error: + frame_iter = () + for current_frame in frame_iter: + pc = current_frame.address() + 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(pc), } - 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. - return { - "stackFrames": frames, - } + objfile = gdb.current_progspace().objfile_for_address(pc) + if objfile is not None: + newframe["moduleId"] = module_id(objfile) + 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, + } + 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. + return { + "stackFrames": frames, + } @request("stackTrace") @capability("supportsDelayedStackTraceLoading") -def stacktrace(*, levels: int = 0, startFrame: int = 0, threadId: int, **extra): - return send_gdb_with_response(lambda: _backtrace(threadId, levels, startFrame)) +def stacktrace( + *, levels: int = 0, startFrame: int = 0, threadId: int, format=None, **extra +): + return send_gdb_with_response( + lambda: _backtrace(threadId, levels, startFrame, format) + ) diff --git a/gdb/python/lib/gdb/dap/evaluate.py b/gdb/python/lib/gdb/dap/evaluate.py index 9fa94e0..405e6fc 100644 --- a/gdb/python/lib/gdb/dap/evaluate.py +++ b/gdb/python/lib/gdb/dap/evaluate.py @@ -21,7 +21,7 @@ from typing import Optional 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 +from .varref import find_variable, VariableReference, apply_format class EvaluateResult(VariableReference): @@ -31,24 +31,25 @@ class EvaluateResult(VariableReference): # Helper function to evaluate an expression in a certain frame. @in_gdb_thread -def _evaluate(expr, frame_id): - global_context = True - if frame_id is not None: - select_frame(frame_id) - global_context = False - val = gdb.parse_and_eval(expr, global_context=global_context) - ref = EvaluateResult(val) - return ref.to_object() +def _evaluate(expr, frame_id, value_format): + with apply_format(value_format): + global_context = True + if frame_id is not None: + select_frame(frame_id) + global_context = False + val = gdb.parse_and_eval(expr, global_context=global_context) + ref = EvaluateResult(val) + return ref.to_object() # Like _evaluate but ensure that the expression cannot cause side # effects. @in_gdb_thread -def _eval_for_hover(expr, frame_id): +def _eval_for_hover(expr, frame_id, value_format): with gdb.with_parameter("may-write-registers", "off"): with gdb.with_parameter("may-write-memory", "off"): with gdb.with_parameter("may-call-functions", "off"): - return _evaluate(expr, frame_id) + return _evaluate(expr, frame_id, value_format) class _SetResult(VariableReference): @@ -58,15 +59,16 @@ class _SetResult(VariableReference): # Helper function to perform an assignment. @in_gdb_thread -def _set_expression(expression, value, frame_id): - global_context = True - if frame_id is not None: - 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) - lhs.assign(rhs) - return _SetResult(lhs).to_object() +def _set_expression(expression, value, frame_id, value_format): + with apply_format(value_format): + global_context = True + if frame_id is not None: + 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) + lhs.assign(rhs) + return _SetResult(lhs).to_object() # Helper function to evaluate a gdb command in a certain frame. @@ -83,42 +85,50 @@ def _repl(command, frame_id): @request("evaluate") @capability("supportsEvaluateForHovers") +@capability("supportsValueFormattingOptions") def eval_request( *, expression: str, frameId: Optional[int] = None, context: str = "variables", + format=None, **args, ): if context in ("watch", "variables"): # These seem to be expression-like. - return send_gdb_with_response(lambda: _evaluate(expression, frameId)) + return send_gdb_with_response(lambda: _evaluate(expression, frameId, format)) elif context == "hover": - return send_gdb_with_response(lambda: _eval_for_hover(expression, frameId)) + return send_gdb_with_response( + lambda: _eval_for_hover(expression, frameId, format) + ) elif context == "repl": + # Ignore the format for repl evaluation. return send_gdb_with_response(lambda: _repl(expression, frameId)) else: raise Exception('unknown evaluate context "' + context + '"') @in_gdb_thread -def _variables(ref, start, count): - var = find_variable(ref) - children = var.fetch_children(start, count) - return [x.to_object() for x in children] +def _variables(ref, start, count, value_format): + with apply_format(value_format): + var = find_variable(ref) + children = var.fetch_children(start, count) + return [x.to_object() for x in children] @request("variables") # Note that we ignore the 'filter' field. That seems to be # specific to javascript. -def variables(*, variablesReference: int, start: int = 0, count: int = 0, **args): +def variables( + *, variablesReference: int, start: int = 0, count: int = 0, format=None, **args +): # This behavior was clarified here: # https://github.com/microsoft/debug-adapter-protocol/pull/394 if not client_bool_capability("supportsVariablePaging"): start = 0 count = 0 result = send_gdb_with_response( - lambda: _variables(variablesReference, start, count) + lambda: _variables(variablesReference, start, count, format) ) return {"variables": result} @@ -126,6 +136,8 @@ def variables(*, variablesReference: int, start: int = 0, count: int = 0, **args @capability("supportsSetExpression") @request("setExpression") def set_expression( - *, expression: str, value: str, frameId: Optional[int] = None, **args + *, expression: str, value: str, frameId: Optional[int] = None, format=None, **args ): - return send_gdb_with_response(lambda: _set_expression(expression, value, frameId)) + return send_gdb_with_response( + lambda: _set_expression(expression, value, frameId, format) + ) diff --git a/gdb/python/lib/gdb/dap/varref.py b/gdb/python/lib/gdb/dap/varref.py index 0e0d92a..c1e5ba8 100644 --- a/gdb/python/lib/gdb/dap/varref.py +++ b/gdb/python/lib/gdb/dap/varref.py @@ -17,6 +17,7 @@ import gdb from .startup import in_gdb_thread from .server import client_bool_capability from abc import abstractmethod +from contextlib import contextmanager # A list of all the variable references created during this pause. @@ -34,6 +35,23 @@ def clear_vars(event): gdb.events.cont.connect(clear_vars) +# A null context manager. Python supplies one, starting in 3.7. +@contextmanager +def _null(**ignore): + yield + + +@in_gdb_thread +def apply_format(value_format): + """Temporarily apply the DAP ValueFormat. + + This returns a new context manager that applies the given DAP + ValueFormat object globally, then restores gdb's state when finished.""" + if value_format is not None and "hex" in value_format and value_format["hex"]: + return gdb.with_parameter("output-radix", 16) + return _null() + + class BaseReference: """Represent a variable or a scope. diff --git a/gdb/testsuite/gdb.dap/hover.exp b/gdb/testsuite/gdb.dap/hover.exp index 4caf105..5186c4e 100644 --- a/gdb/testsuite/gdb.dap/hover.exp +++ b/gdb/testsuite/gdb.dap/hover.exp @@ -48,6 +48,16 @@ set obj [dap_check_request_and_response "evaluate global" \ dap_match_values "global value in function" [lindex $obj 0] \ "body result" 23 +set obj [dap_check_request_and_response "evaluate global as hex" \ + evaluate {o expression [s global_variable] format [o hex [l true]]}] +dap_match_values "global value in function as hex" [lindex $obj 0] \ + "body result" 0x17 + +set obj [dap_check_request_and_response "evaluate global in hover mode" \ + evaluate {o context [s hover] expression [s global_variable]}] +dap_match_values "global value in hover mode" [lindex $obj 0] \ + "body result" 23 + set obj [dap_request_and_response \ evaluate {o context [s hover] expression [s increment()]}] gdb_assert {[dict get [lindex $obj 0] success] == "false"} \ |