diff options
Diffstat (limited to 'gdb/python/lib/gdb/dap/bt.py')
-rw-r--r-- | gdb/python/lib/gdb/dap/bt.py | 110 |
1 files changed, 90 insertions, 20 deletions
diff --git a/gdb/python/lib/gdb/dap/bt.py b/gdb/python/lib/gdb/dap/bt.py index 982d501..c63ca6d 100644 --- a/gdb/python/lib/gdb/dap/bt.py +++ b/gdb/python/lib/gdb/dap/bt.py @@ -16,37 +16,68 @@ import gdb import os -from gdb.frames import frame_iterator -from .frames import frame_id +# This is deprecated in 3.9, but required in older versions. +from typing import Optional + +from .frames import dap_frame_generator from .modules import module_id +from .scopes import symbol_value from .server import request, capability from .sources import make_source from .startup import send_gdb_with_response, in_gdb_thread from .state import set_thread +from .typecheck import type_check from .varref import apply_format +# Helper function to compute parameter information for a stack frame. +@in_gdb_thread +def _compute_parameters(frame, stack_format): + arg_iter = frame.frame_args() + if arg_iter is None: + return "" + result = [] + for arg in arg_iter: + desc = [] + name, val = symbol_value(arg, frame) + # We don't try to use any particular language's syntax for the + # output here. + if stack_format["parameterTypes"]: + desc.append("[" + str(val.type) + "]") + if stack_format["parameterNames"]: + desc.append(name) + # If both the name and the value are requested, insert an + # '=' for clarity. + if stack_format["parameterValues"]: + desc.append("=") + if stack_format["parameterValues"]: + desc.append(val.format_string(summary=True)) + result.append(" ".join(desc)) + return ", ".join(result) + + # Helper function to compute a stack trace. @in_gdb_thread -def _backtrace(thread_id, levels, startFrame, value_format): - with apply_format(value_format): +def _backtrace(thread_id, levels, startFrame, stack_format): + with apply_format(stack_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: + frame_iter = dap_frame_generator(startFrame, levels, stack_format["includeAll"]) + for frame_id, current_frame in frame_iter: pc = current_frame.address() + # The stack frame format affects the name, so we build it up + # piecemeal and assign it at the end. + name = current_frame.function() + # The meaning of StackFrameFormat.parameters was clarified + # in https://github.com/microsoft/debug-adapter-protocol/issues/411. + if stack_format["parameters"] and ( + stack_format["parameterTypes"] + or stack_format["parameterNames"] + or stack_format["parameterValues"] + ): + name += "(" + _compute_parameters(current_frame, stack_format) + ")" newframe = { - "id": frame_id(current_frame), - "name": current_frame.function(), + "id": frame_id, # This must always be supplied, but we will set it # correctly later if that is possible. "line": 0, @@ -54,15 +85,20 @@ def _backtrace(thread_id, levels, startFrame, value_format): "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 + if stack_format["line"]: + name += ", line " + str(line) + objfile = gdb.current_progspace().objfile_for_address(pc) + if objfile is not None: + newframe["moduleId"] = module_id(objfile) + if stack_format["module"]: + name += ", module " + objfile.username filename = current_frame.filename() if filename is not None: newframe["source"] = make_source(filename, os.path.basename(filename)) + newframe["name"] = name 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 @@ -72,11 +108,45 @@ def _backtrace(thread_id, levels, startFrame, value_format): } +# A helper function that checks the types of the elements of a +# StackFrameFormat, and converts this to a dict where all the members +# are set. This simplifies the implementation code a bit. +@type_check +def check_stack_frame( + *, + # Note that StackFrameFormat extends ValueFormat, which is why + # "hex" appears here. + hex: Optional[bool] = False, + parameters: Optional[bool] = False, + parameterTypes: Optional[bool] = False, + parameterNames: Optional[bool] = False, + parameterValues: Optional[bool] = False, + line: Optional[bool] = False, + module: Optional[bool] = False, + includeAll: Optional[bool] = False, + **rest +): + return { + "hex": hex, + "parameters": parameters, + "parameterTypes": parameterTypes, + "parameterNames": parameterNames, + "parameterValues": parameterValues, + "line": line, + "module": module, + "includeAll": includeAll, + } + + @request("stackTrace") @capability("supportsDelayedStackTraceLoading") def stacktrace( *, levels: int = 0, startFrame: int = 0, threadId: int, format=None, **extra ): + # It's simpler if the format is always set. + if format is None: + format = {} + format = check_stack_frame(**format) return send_gdb_with_response( lambda: _backtrace(threadId, levels, startFrame, format) ) |