diff options
-rw-r--r-- | gdb/NEWS | 5 | ||||
-rw-r--r-- | gdb/data-directory/Makefile.in | 1 | ||||
-rw-r--r-- | gdb/python/lib/gdb/dap/globalvars.py | 97 | ||||
-rw-r--r-- | gdb/python/lib/gdb/dap/scopes.py | 4 | ||||
-rw-r--r-- | gdb/testsuite/gdb.dap/ptrref.exp | 8 | ||||
-rw-r--r-- | gdb/testsuite/gdb.dap/rust-slices.exp | 4 |
6 files changed, 115 insertions, 4 deletions
@@ -3,6 +3,11 @@ *** Changes since GDB 15 +* Debugger Adapter Protocol changes + + ** The "scopes" request will now return a scope holding global + variables from the stack frame's compilation unit. + *** Changes in GDB 15 * The MPX commands "show/set mpx bound" have been deprecated, as Intel diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in index 98a4352..f529656 100644 --- a/gdb/data-directory/Makefile.in +++ b/gdb/data-directory/Makefile.in @@ -98,6 +98,7 @@ PYTHON_FILE_LIST = \ gdb/dap/evaluate.py \ gdb/dap/events.py \ gdb/dap/frames.py \ + gdb/dap/globalvars.py \ gdb/dap/__init__.py \ gdb/dap/io.py \ gdb/dap/launch.py \ diff --git a/gdb/python/lib/gdb/dap/globalvars.py b/gdb/python/lib/gdb/dap/globalvars.py new file mode 100644 index 0000000..149c9a8 --- /dev/null +++ b/gdb/python/lib/gdb/dap/globalvars.py @@ -0,0 +1,97 @@ +# Copyright 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/>. + +import gdb + +from .sources import make_source +from .startup import in_gdb_thread +from .varref import BaseReference + +# Map a block identifier to a scope object. +_id_to_scope = {} + + +# Arrange to clear the scope references when the inferior runs. +@in_gdb_thread +def clear(event): + global _id_to_scope + _id_to_scope = {} + + +gdb.events.cont.connect(clear) + + +# A scope that holds static and/or global variables. +class _Globals(BaseReference): + def __init__(self, filename, var_list): + super().__init__("Globals") + self.filename = filename + self.var_list = var_list + + def to_object(self): + result = super().to_object() + result["presentationHint"] = "globals" + # How would we know? + result["expensive"] = False + result["namedVariables"] = self.child_count() + if self.filename is not None: + result["source"] = make_source(self.filename) + return result + + def has_children(self): + # This object won't even be created if there are no variables + # to return. + return True + + def child_count(self): + return len(self.var_list) + + @in_gdb_thread + def fetch_one_child(self, idx): + return self.var_list[idx].value() + + +@in_gdb_thread +def get_global_scope(frame): + """Given a frame decorator, return the corresponding global scope + object. + + If the frame does not have a block, or if the CU does not have + globals (that is, empty static and global blocks), return None.""" + inf_frame = frame.inferior_frame() + # It's unfortunate that this API throws instead of returning None. + try: + block = inf_frame.block() + except RuntimeError: + return None + + global _id_to_scope + block = block.static_block + if block in _id_to_scope: + return _id_to_scope[block] + + syms = [] + block_iter = block + while block_iter is not None: + syms += [sym for sym in block_iter if sym.is_variable] + block_iter = block_iter.superblock + + if len(syms) == 0: + return None + + result = _Globals(frame.filename(), syms) + _id_to_scope[block] = result + + return result diff --git a/gdb/python/lib/gdb/dap/scopes.py b/gdb/python/lib/gdb/dap/scopes.py index 09f440e..d0e9115 100644 --- a/gdb/python/lib/gdb/dap/scopes.py +++ b/gdb/python/lib/gdb/dap/scopes.py @@ -16,6 +16,7 @@ import gdb from .frames import frame_for_id +from .globalvars import get_global_scope from .server import request from .sources import make_source from .startup import in_gdb_thread @@ -164,4 +165,7 @@ def scopes(*, frameId: int, **extra): scopes.append(_ScopeReference("Locals", "locals", frame, locs)) scopes.append(_RegisterReference("Registers", frame)) frame_to_scope[frameId] = scopes + global_scope = get_global_scope(frame) + if global_scope is not None: + scopes.append(global_scope) return {"scopes": [x.to_object() for x in scopes]} diff --git a/gdb/testsuite/gdb.dap/ptrref.exp b/gdb/testsuite/gdb.dap/ptrref.exp index 0552c3b..236ffae 100644 --- a/gdb/testsuite/gdb.dap/ptrref.exp +++ b/gdb/testsuite/gdb.dap/ptrref.exp @@ -54,12 +54,14 @@ set scopes [dap_check_request_and_response "get scopes" scopes \ [format {o frameId [i %d]} $frame_id]] set scopes [dict get [lindex $scopes 0] body scopes] -gdb_assert {[llength $scopes] == 2} "two scopes" +gdb_assert {[llength $scopes] == 3} "three scopes" -lassign $scopes scope reg_scope +lassign $scopes scope reg_scope global_scope gdb_assert {[dict get $scope name] == "Locals"} "scope is locals" +gdb_assert {[dict get $global_scope name] == "Globals"} "scope is globals" -gdb_assert {[dict get $scope namedVariables] == 4} "three vars in scope" +gdb_assert {[dict get $scope namedVariables] == 4} "four vars in locals" +gdb_assert {[dict get $global_scope namedVariables] == 1} "one var in globals" set num [dict get $scope variablesReference] set refs [lindex [dap_check_request_and_response "fetch variables" \ diff --git a/gdb/testsuite/gdb.dap/rust-slices.exp b/gdb/testsuite/gdb.dap/rust-slices.exp index c85568d..d3bd305 100644 --- a/gdb/testsuite/gdb.dap/rust-slices.exp +++ b/gdb/testsuite/gdb.dap/rust-slices.exp @@ -59,7 +59,9 @@ set scopes [dap_check_request_and_response "get scopes" scopes \ [format {o frameId [i %d]} $frame_id]] set scopes [dict get [lindex $scopes 0] body scopes] -gdb_assert {[llength $scopes] == 2} "two scopes" +# There are three scopes because an artificial symbol ends up in the +# DWARF. See https://github.com/rust-lang/rust/issues/125126. +gdb_assert {[llength $scopes] == 3} "three scopes" lassign $scopes scope ignore gdb_assert {[dict get $scope name] == "Locals"} "scope is locals" |