aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/NEWS5
-rw-r--r--gdb/data-directory/Makefile.in1
-rw-r--r--gdb/python/lib/gdb/dap/globalvars.py97
-rw-r--r--gdb/python/lib/gdb/dap/scopes.py4
-rw-r--r--gdb/testsuite/gdb.dap/ptrref.exp8
-rw-r--r--gdb/testsuite/gdb.dap/rust-slices.exp4
6 files changed, 115 insertions, 4 deletions
diff --git a/gdb/NEWS b/gdb/NEWS
index 408d150..bb7c4a6 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -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"