GDB (xrefs)
Loading...
Searching...
No Matches
varref.py
Go to the documentation of this file.
1# Copyright 2023 Free Software Foundation, Inc.
2
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 3 of the License, or
6# (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program. If not, see <http://www.gnu.org/licenses/>.
15
16import gdb
17from .startup import in_gdb_thread
18from .server import client_bool_capability
19from abc import ABC, abstractmethod
20from collections import defaultdict
21from contextlib import contextmanager
22
23
24# A list of all the variable references created during this pause.
25all_variables = []
26
27
28# When the inferior is re-started, we erase all variable references.
29# See the section "Lifetime of Objects References" in the spec.
30@in_gdb_thread
31def clear_vars(event):
32 global all_variables
33 all_variables = []
34
35
36gdb.events.cont.connect(clear_vars)
37
38
39# A null context manager. Python supplies one, starting in 3.7.
40@contextmanager
41def _null(**ignore):
42 yield
43
44
45@in_gdb_thread
46def apply_format(value_format):
47 """Temporarily apply the DAP ValueFormat.
48
49 This returns a new context manager that applies the given DAP
50 ValueFormat object globally, then restores gdb's state when finished."""
51 if value_format is not None and "hex" in value_format and value_format["hex"]:
52 return gdb.with_parameter("output-radix", 16)
53 return _null()
54
55
56class BaseReference(ABC):
57 """Represent a variable or a scope.
58
59 This class is just a base class, some methods must be implemented in
60 subclasses.
61
62 The 'ref' field can be used as the variablesReference in the protocol.
63 """
64
65 @in_gdb_thread
66 def __init__(self, name):
67 """Create a new variable reference with the given name.
68
69 NAME is a string or None. None means this does not have a
70 name, e.g., the result of expression evaluation."""
71
72 global all_variables
73 all_variables.append(self)
74 self.ref = len(all_variables)
75 self.name = name
77
78 @in_gdb_thread
79 def to_object(self):
80 """Return a dictionary that describes this object for DAP.
81
82 The resulting object is a starting point that can be filled in
83 further. See the Scope or Variable types in the spec"""
84 result = {"variablesReference": self.ref if self.has_childrenhas_children() else 0}
85 if self.name is not None:
86 result["name"] = str(self.name)
87 return result
88
89 @abstractmethod
90 def has_children(self):
91 """Return True if this object has children."""
92 return False
93
94 def reset_children(self):
95 """Reset any cached information about the children of this object."""
96 # A list of all the children. Each child is a BaseReference
97 # of some kind.
98 self.children = None
99 # Map from the name of a child to a BaseReference.
100 self.by_name = {}
101 # Keep track of how many duplicates there are of a given name,
102 # so that unique names can be generated. Map from base name
103 # to a count.
104 self.name_counts = defaultdict(lambda: 1)
105
106 @abstractmethod
107 def fetch_one_child(self, index):
108 """Fetch one child of this variable.
109
110 INDEX is the index of the child to fetch.
111 This should return a tuple of the form (NAME, VALUE), where
112 NAME is the name of the variable, and VALUE is a gdb.Value."""
113 return
114
115 @abstractmethod
116 def child_count(self):
117 """Return the number of children of this variable."""
118 return
119
120 # Helper method to compute the final name for a child whose base
121 # name is given. Updates the name_counts map. This is used to
122 # handle shadowing -- in DAP, the adapter is responsible for
123 # making sure that all the variables in a a given container have
124 # unique names. See
125 # https://github.com/microsoft/debug-adapter-protocol/issues/141
126 # and
127 # https://github.com/microsoft/debug-adapter-protocol/issues/149
128 def _compute_name(self, name):
129 if name in self.by_name:
130 self.name_counts[name] += 1
131 # In theory there's no safe way to compute a name, because
132 # a pretty-printer might already be generating names of
133 # that form. In practice I think we should not worry too
134 # much.
135 name = name + " #" + str(self.name_counts[name])
136 return name
137
138 @in_gdb_thread
139 def fetch_children(self, start, count):
140 """Fetch children of this variable.
141
142 START is the starting index.
143 COUNT is the number to return, with 0 meaning return all.
144 Returns an iterable of some kind."""
145 if count == 0:
146 count = self.child_countchild_count()
147 if self.children is None:
148 self.children = [None] * self.child_countchild_count()
149 for idx in range(start, start + count):
150 if self.children[idx] is None:
151 (name, value) = self.fetch_one_childfetch_one_child(idx)
152 name = self._compute_name_compute_name(name)
153 var = VariableReference(name, value)
154 self.children[idx] = var
155 self.by_name[name] = var
156 yield self.children[idx]
157
158 @in_gdb_thread
159 def find_child_by_name(self, name):
160 """Find a child of this variable, given its name.
161
162 Returns the value of the child, or throws if not found."""
163 # A lookup by name can only be done using names previously
164 # provided to the client, so we can simply rely on the by-name
165 # map here.
166 if name in self.by_name:
167 return self.by_name[name]
168 raise Exception("no variable named '" + name + "'")
169
170
172 """Concrete subclass of BaseReference that handles gdb.Value."""
173
174 def __init__(self, name, value, result_name="value"):
175 """Initializer.
176
177 NAME is the name of this reference, see superclass.
178 VALUE is a gdb.Value that holds the value.
179 RESULT_NAME can be used to change how the simple string result
180 is emitted in the result dictionary."""
181 super().__init__(name)
182 self.result_name = result_name
183 self.value = value
185
186 # Internal method to update local data when the value changes.
187 def _update_value(self):
190 self.child_cache = None
192 self.count = -1
193 else:
194 self.count = None
195
196 def assign(self, value):
197 """Assign VALUE to this object and update."""
198 self.value.assign(value)
200
201 def has_children(self):
202 return hasattr(self.printer, "children")
203
204 def cache_children(self):
205 if self.child_cache is None:
206 # This discards all laziness. This could be improved
207 # slightly by lazily evaluating children, but because this
208 # code also generally needs to know the number of
209 # children, it probably wouldn't help much. Note that
210 # this is only needed with legacy (non-ValuePrinter)
211 # printers.
212 self.child_cache = list(self.printer.children())
213 return self.child_cache
214
215 def child_count(self):
216 if self.count is None:
217 return None
218 if self.count == -1:
219 num_children = None
220 if isinstance(self.printer, gdb.ValuePrinter) and hasattr(
221 self.printer, "num_children"
222 ):
223 num_children = self.printer.num_children()
224 if num_children is None:
225 num_children = len(self.cache_childrencache_children())
226 self.count = num_children
227 return self.count
228
229 def to_object(self):
230 result = super().to_object()
231 result[self.result_name] = str(self.printer.to_string())
233 if num_children is not None:
234 if (
235 hasattr(self.printer, "display_hint")
236 and self.printer.display_hint() == "array"
237 ):
238 result["indexedVariables"] = num_children
239 else:
240 result["namedVariables"] = num_children
241 if client_bool_capability("supportsMemoryReferences"):
242 # https://github.com/microsoft/debug-adapter-protocol/issues/414
243 # changed DAP to allow memory references for any of the
244 # variable response requests, and to lift the restriction
245 # to pointer-to-function from Variable.
246 if self.value.type.strip_typedefs().code == gdb.TYPE_CODE_PTR:
247 result["memoryReference"] = hex(int(self.value))
248 if client_bool_capability("supportsVariableType"):
249 result["type"] = str(self.value.type)
250 return result
251
252 @in_gdb_thread
253 def fetch_one_child(self, idx):
254 if isinstance(self.printer, gdb.ValuePrinter) and hasattr(
255 self.printer, "child"
256 ):
257 (name, val) = self.printer.child(idx)
258 else:
259 (name, val) = self.cache_childrencache_children()[idx]
260 # A pretty-printer can return something other than a
261 # gdb.Value, but it must be convertible.
262 if not isinstance(val, gdb.Value):
263 val = gdb.Value(val)
264 return (name, val)
265
266
267@in_gdb_thread
269 """Given a variable reference, return the corresponding variable object."""
270 global all_variables
271 # Variable references are offset by 1.
272 ref = ref - 1
273 if ref < 0 or ref > len(all_variables):
274 raise Exception("invalid variablesReference")
275 return all_variables[ref]
find_child_by_name(self, name)
Definition varref.py:159
fetch_children(self, start, count)
Definition varref.py:139
fetch_one_child(self, index)
Definition varref.py:107
__init__(self, name, value, result_name="value")
Definition varref.py:174
std::string to_string(cooked_index_flag flags)
clear_vars(event)
Definition varref.py:31
_null(**ignore)
Definition varref.py:41
find_variable(ref)
Definition varref.py:268
apply_format(value_format)
Definition varref.py:46
make_visualizer(value)
Definition printing.py:357
with_parameter(name, value)
Definition __init__.py:254
Definition value.h:90