forked from Imagelibrary/binutils-gdb
A comment in bugzilla pointed out a bug in my earlier patch to handle
the DAP "linesStartAt1" setting. In particular, in the backtrace
code, "line" can be None, which would lead to an exception from
export_line.
This patch fixes the problem.
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32468
(cherry picked from commit 28e5851344)
153 lines
5.7 KiB
Python
153 lines
5.7 KiB
Python
# Copyright 2022-2024 Free Software Foundation, Inc.
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
# This is deprecated in 3.9, but required in older versions.
|
|
from typing import Optional
|
|
|
|
import gdb
|
|
|
|
from .frames import dap_frame_generator
|
|
from .modules import module_id
|
|
from .scopes import symbol_value
|
|
from .server import capability, export_line, request
|
|
from .sources import make_source
|
|
from .startup import 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, stack_format):
|
|
with apply_format(stack_format):
|
|
set_thread(thread_id)
|
|
frames = []
|
|
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,
|
|
# 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),
|
|
}
|
|
line = current_frame.line()
|
|
if line is not None:
|
|
newframe["line"] = export_line(line)
|
|
if stack_format["line"]:
|
|
# Unclear whether export_line should be called
|
|
# here, but since it's just for users we pick the
|
|
# gdb representation.
|
|
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)
|
|
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
|
|
# response yields fewer frames than requested.
|
|
return {
|
|
"stackFrames": frames,
|
|
}
|
|
|
|
|
|
# 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 _backtrace(threadId, levels, startFrame, format)
|