]>
Commit | Line | Data |
---|---|---|
cdd1ba62 | 1 | # Pretty-printers for libstdc++. |
41850419 | 2 | |
83ffe9cd | 3 | # Copyright (C) 2008-2023 Free Software Foundation, Inc. |
41850419 TT |
4 | |
5 | # This program is free software; you can redistribute it and/or modify | |
6 | # it under the terms of the GNU General Public License as published by | |
7 | # the Free Software Foundation; either version 3 of the License, or | |
8 | # (at your option) any later version. | |
9 | # | |
10 | # This program is distributed in the hope that it will be useful, | |
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | # GNU General Public License for more details. | |
14 | # | |
15 | # You should have received a copy of the GNU General Public License | |
16 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
17 | ||
d1f736a2 | 18 | import gdb |
41850419 TT |
19 | import itertools |
20 | import re | |
2db38d9f | 21 | import sys, os, errno |
80ff207d | 22 | import datetime |
cdd1ba62 SB |
23 | |
24 | ### Python 2 + Python 3 compatibility code | |
25 | ||
26 | # Resources about compatibility: | |
27 | # | |
28 | # * <http://pythonhosted.org/six/>: Documentation of the "six" module | |
29 | ||
30 | # FIXME: The handling of e.g. std::basic_string (at least on char) | |
31 | # probably needs updating to work with Python 3's new string rules. | |
32 | # | |
33 | # In particular, Python 3 has a separate type (called byte) for | |
34 | # bytestrings, and a special b"" syntax for the byte literals; the old | |
35 | # str() type has been redefined to always store Unicode text. | |
36 | # | |
37 | # We probably can't do much about this until this GDB PR is addressed: | |
38 | # <https://sourceware.org/bugzilla/show_bug.cgi?id=17138> | |
39 | ||
40 | if sys.version_info[0] > 2: | |
41 | ### Python 3 stuff | |
42 | Iterator = object | |
43 | # Python 3 folds these into the normal functions. | |
44 | imap = map | |
45 | izip = zip | |
46 | # Also, int subsumes long | |
47 | long = int | |
80ff207d | 48 | _utc_timezone = datetime.timezone.utc |
cdd1ba62 SB |
49 | else: |
50 | ### Python 2 stuff | |
51 | class Iterator: | |
52 | """Compatibility mixin for iterators | |
53 | ||
54 | Instead of writing next() methods for iterators, write | |
55 | __next__() methods and use this mixin to make them work in | |
56 | Python 2 as well as Python 3. | |
57 | ||
58 | Idea stolen from the "six" documentation: | |
59 | <http://pythonhosted.org/six/#six.Iterator> | |
60 | """ | |
61 | ||
62 | def next(self): | |
63 | return self.__next__() | |
64 | ||
65 | # In Python 2, we still need these from itertools | |
66 | from itertools import imap, izip | |
41850419 | 67 | |
80ff207d JW |
68 | # Python 2 does not provide the datetime.UTC singleton. |
69 | class UTC(datetime.tzinfo): | |
70 | """Concrete tzinfo class representing the UTC time zone""" | |
71 | ||
72 | def utcoffset(self, dt): | |
73 | return datetime.timedelta(0) | |
74 | ||
75 | def tzname(self, dt): | |
76 | return "UTC" | |
77 | ||
78 | def dst(self, dt): | |
79 | return datetime.timedelta(0) | |
80 | _utc_timezone = UTC() | |
81 | ||
d63c53cc TT |
82 | # Try to use the new-style pretty-printing if available. |
83 | _use_gdb_pp = True | |
84 | try: | |
85 | import gdb.printing | |
86 | except ImportError: | |
87 | _use_gdb_pp = False | |
88 | ||
50605a7f TT |
89 | # Try to install type-printers. |
90 | _use_type_printing = False | |
91 | try: | |
92 | import gdb.types | |
93 | if hasattr(gdb.types, 'TypePrinter'): | |
94 | _use_type_printing = True | |
95 | except ImportError: | |
96 | pass | |
97 | ||
3efe2bf7 TT |
98 | # Starting with the type ORIG, search for the member type NAME. This |
99 | # handles searching upward through superclasses. This is needed to | |
100 | # work around http://sourceware.org/bugzilla/show_bug.cgi?id=13615. | |
101 | def find_type(orig, name): | |
102 | typ = orig.strip_typedefs() | |
103 | while True: | |
9dacc828 JW |
104 | # Use Type.tag to ignore cv-qualifiers. PR 67440. |
105 | search = '%s::%s' % (typ.tag, name) | |
3efe2bf7 TT |
106 | try: |
107 | return gdb.lookup_type(search) | |
108 | except RuntimeError: | |
109 | pass | |
110 | # The type was not found, so try the superclass. We only need | |
111 | # to check the first superclass, so we don't bother with | |
112 | # anything fancier here. | |
9d50a6a7 JW |
113 | fields = typ.fields() |
114 | if len(fields) and fields[0].is_base_class: | |
115 | typ = fields[0].type | |
116 | else: | |
47059228 | 117 | raise ValueError("Cannot find type %s::%s" % (str(orig), name)) |
3efe2bf7 | 118 | |
87c7063d | 119 | _versioned_namespace = '__8::' |
f9a27859 | 120 | |
9d50a6a7 JW |
121 | def lookup_templ_spec(templ, *args): |
122 | """ | |
123 | Lookup template specialization templ<args...> | |
124 | """ | |
125 | t = '{}<{}>'.format(templ, ', '.join([str(a) for a in args])) | |
126 | try: | |
127 | return gdb.lookup_type(t) | |
128 | except gdb.error as e: | |
129 | # Type not found, try again in versioned namespace. | |
130 | global _versioned_namespace | |
131 | if _versioned_namespace and _versioned_namespace not in templ: | |
132 | t = t.replace('::', '::' + _versioned_namespace, 1) | |
133 | try: | |
134 | return gdb.lookup_type(t) | |
135 | except gdb.error: | |
136 | # If that also fails, rethrow the original exception | |
137 | pass | |
138 | raise e | |
139 | ||
140 | # Use this to find container node types instead of find_type, | |
141 | # see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91997 for details. | |
142 | def lookup_node_type(nodename, containertype): | |
143 | """ | |
144 | Lookup specialization of template NODENAME corresponding to CONTAINERTYPE. | |
145 | e.g. if NODENAME is '_List_node' and CONTAINERTYPE is std::list<int> | |
146 | then return the type std::_List_node<int>. | |
147 | Returns None if not found. | |
148 | """ | |
149 | # If nodename is unqualified, assume it's in namespace std. | |
150 | if '::' not in nodename: | |
151 | nodename = 'std::' + nodename | |
152 | try: | |
153 | valtype = find_type(containertype, 'value_type') | |
154 | except: | |
155 | valtype = containertype.template_argument(0) | |
156 | valtype = valtype.strip_typedefs() | |
157 | try: | |
158 | return lookup_templ_spec(nodename, valtype) | |
159 | except gdb.error as e: | |
160 | # For debug mode containers the node is in std::__cxx1998. | |
161 | if is_member_of_namespace(nodename, 'std'): | |
162 | if is_member_of_namespace(containertype, 'std::__cxx1998', | |
163 | 'std::__debug', '__gnu_debug'): | |
164 | nodename = nodename.replace('::', '::__cxx1998::', 1) | |
9d50a6a7 JW |
165 | try: |
166 | return lookup_templ_spec(nodename, valtype) | |
167 | except gdb.error: | |
168 | pass | |
169 | return None | |
170 | ||
171 | def is_member_of_namespace(typ, *namespaces): | |
172 | """ | |
173 | Test whether a type is a member of one of the specified namespaces. | |
174 | The type can be specified as a string or a gdb.Type object. | |
175 | """ | |
176 | if type(typ) is gdb.Type: | |
177 | typ = str(typ) | |
178 | typ = strip_versioned_namespace(typ) | |
179 | for namespace in namespaces: | |
180 | if typ.startswith(namespace + '::'): | |
181 | return True | |
182 | return False | |
183 | ||
478490f6 | 184 | def is_specialization_of(x, template_name): |
92281622 JW |
185 | """ |
186 | Test whether a type is a specialization of the named class template. | |
187 | The type can be specified as a string or a gdb.Type object. | |
188 | The template should be the name of a class template as a string, | |
189 | without any 'std' qualification. | |
190 | """ | |
f9a27859 | 191 | global _versioned_namespace |
478490f6 JW |
192 | if type(x) is gdb.Type: |
193 | x = x.tag | |
f9a27859 | 194 | if _versioned_namespace: |
478490f6 JW |
195 | return re.match('^std::(%s)?%s<.*>$' % (_versioned_namespace, template_name), x) is not None |
196 | return re.match('^std::%s<.*>$' % template_name, x) is not None | |
f9a27859 FD |
197 | |
198 | def strip_versioned_namespace(typename): | |
199 | global _versioned_namespace | |
200 | if _versioned_namespace: | |
201 | return typename.replace(_versioned_namespace, '') | |
202 | return typename | |
203 | ||
bab0a26d JW |
204 | def strip_inline_namespaces(type_str): |
205 | "Remove known inline namespaces from the canonical name of a type." | |
206 | type_str = strip_versioned_namespace(type_str) | |
207 | type_str = type_str.replace('std::__cxx11::', 'std::') | |
208 | expt_ns = 'std::experimental::' | |
209 | for lfts_ns in ('fundamentals_v1', 'fundamentals_v2'): | |
210 | type_str = type_str.replace(expt_ns+lfts_ns+'::', expt_ns) | |
211 | fs_ns = expt_ns + 'filesystem::' | |
212 | type_str = type_str.replace(fs_ns+'v1::', fs_ns) | |
213 | return type_str | |
214 | ||
215 | def get_template_arg_list(type_obj): | |
216 | "Return a type's template arguments as a list" | |
217 | n = 0 | |
218 | template_args = [] | |
219 | while True: | |
220 | try: | |
221 | template_args.append(type_obj.template_argument(n)) | |
222 | except: | |
223 | return template_args | |
224 | n += 1 | |
225 | ||
d2dfcf82 JO |
226 | class SmartPtrIterator(Iterator): |
227 | "An iterator for smart pointer types with a single 'child' value" | |
228 | ||
229 | def __init__(self, val): | |
230 | self.val = val | |
231 | ||
232 | def __iter__(self): | |
233 | return self | |
234 | ||
235 | def __next__(self): | |
236 | if self.val is None: | |
237 | raise StopIteration | |
238 | self.val, val = None, self.val | |
239 | return ('get()', val) | |
240 | ||
a1527f2f | 241 | class SharedPointerPrinter: |
a8495845 | 242 | "Print a shared_ptr, weak_ptr, atomic<shared_ptr>, or atomic<weak_ptr>" |
41850419 TT |
243 | |
244 | def __init__ (self, typename, val): | |
f9a27859 | 245 | self.typename = strip_versioned_namespace(typename) |
41850419 | 246 | self.val = val |
d2dfcf82 JO |
247 | self.pointer = val['_M_ptr'] |
248 | ||
249 | def children (self): | |
250 | return SmartPtrIterator(self.pointer) | |
41850419 | 251 | |
a8495845 JW |
252 | # Return the _Sp_counted_base<>* that holds the refcounts. |
253 | def _get_refcounts (self): | |
254 | if self.typename == 'std::atomic': | |
255 | # A tagged pointer is stored as uintptr_t. | |
256 | ptr_val = self.val['_M_refcount']['_M_val']['_M_i'] | |
257 | ptr_val = ptr_val - (ptr_val % 2) # clear lock bit | |
258 | ptr_type = find_type(self.val['_M_refcount'].type, 'pointer') | |
259 | return ptr_val.cast(ptr_type) | |
260 | return self.val['_M_refcount']['_M_pi'] | |
261 | ||
41850419 | 262 | def to_string (self): |
a1527f2f | 263 | state = 'empty' |
a8495845 JW |
264 | refcounts = self._get_refcounts() |
265 | targ = self.val.type.template_argument(0) | |
634b0089 | 266 | targ = strip_versioned_namespace(str(targ)) |
a8495845 | 267 | |
a1527f2f JW |
268 | if refcounts != 0: |
269 | usecount = refcounts['_M_use_count'] | |
270 | weakcount = refcounts['_M_weak_count'] | |
271 | if usecount == 0: | |
d2dfcf82 | 272 | state = 'expired, weak count %d' % weakcount |
a1527f2f | 273 | else: |
d2dfcf82 | 274 | state = 'use count %d, weak count %d' % (usecount, weakcount - 1) |
634b0089 | 275 | return '%s<%s> (%s)' % (self.typename, targ, state) |
41850419 | 276 | |
a634928f JW |
277 | def _tuple_impl_get(val): |
278 | "Return the tuple element stored in a _Tuple_impl<N, T> base class." | |
279 | bases = val.type.fields() | |
280 | if not bases[-1].is_base_class: | |
281 | raise ValueError("Unsupported implementation for std::tuple: %s" % str(val.type)) | |
282 | # Get the _Head_base<N, T> base class: | |
283 | head_base = val.cast(bases[-1].type) | |
284 | fields = head_base.type.fields() | |
285 | if len(fields) == 0: | |
286 | raise ValueError("Unsupported implementation for std::tuple: %s" % str(val.type)) | |
287 | if fields[0].name == '_M_head_impl': | |
288 | # The tuple element is the _Head_base::_M_head_impl data member. | |
289 | return head_base['_M_head_impl'] | |
290 | elif fields[0].is_base_class: | |
291 | # The tuple element is an empty base class of _Head_base. | |
292 | # Cast to that empty base class. | |
293 | return head_base.cast(fields[0].type) | |
294 | else: | |
295 | raise ValueError("Unsupported implementation for std::tuple: %s" % str(val.type)) | |
296 | ||
297 | def tuple_get(n, val): | |
298 | "Return the result of std::get<n>(val) on a std::tuple" | |
299 | tuple_size = len(get_template_arg_list(val.type)) | |
300 | if n > tuple_size: | |
301 | raise ValueError("Out of range index for std::get<N> on std::tuple") | |
302 | # Get the first _Tuple_impl<0, T...> base class: | |
303 | node = val.cast(val.type.fields()[0].type) | |
304 | while n > 0: | |
305 | # Descend through the base classes until the Nth one. | |
306 | node = node.cast(node.type.fields()[0].type) | |
307 | n -= 1 | |
308 | return _tuple_impl_get(node) | |
309 | ||
310 | def unique_ptr_get(val): | |
311 | "Return the result of val.get() on a std::unique_ptr" | |
312 | # std::unique_ptr<T, D> contains a std::tuple<D::pointer, D>, | |
313 | # either as a direct data member _M_t (the old implementation) | |
314 | # or within a data member of type __uniq_ptr_data. | |
315 | impl_type = val.type.fields()[0].type.strip_typedefs() | |
316 | # Check for new implementations first: | |
317 | if is_specialization_of(impl_type, '__uniq_ptr_data') \ | |
318 | or is_specialization_of(impl_type, '__uniq_ptr_impl'): | |
319 | tuple_member = val['_M_t']['_M_t'] | |
320 | elif is_specialization_of(impl_type, 'tuple'): | |
321 | tuple_member = val['_M_t'] | |
322 | else: | |
323 | raise ValueError("Unsupported implementation for unique_ptr: %s" % str(impl_type)) | |
324 | return tuple_get(0, tuple_member) | |
325 | ||
41850419 TT |
326 | class UniquePointerPrinter: |
327 | "Print a unique_ptr" | |
328 | ||
d63c53cc | 329 | def __init__ (self, typename, val): |
41850419 | 330 | self.val = val |
d2dfcf82 JO |
331 | |
332 | def children (self): | |
a634928f | 333 | return SmartPtrIterator(unique_ptr_get(self.val)) |
d2dfcf82 JO |
334 | |
335 | def to_string (self): | |
8273aa77 | 336 | return ('std::unique_ptr<%s>' % (str(self.val.type.template_argument(0)))) |
41850419 | 337 | |
2dbe56bd JW |
338 | def get_value_from_aligned_membuf(buf, valtype): |
339 | """Returns the value held in a __gnu_cxx::__aligned_membuf.""" | |
340 | return buf['_M_storage'].address.cast(valtype.pointer()).dereference() | |
341 | ||
cc7f3d0e JW |
342 | def get_value_from_list_node(node): |
343 | """Returns the value held in an _List_node<_Val>""" | |
344 | try: | |
345 | member = node.type.fields()[1].name | |
346 | if member == '_M_data': | |
347 | # C++03 implementation, node contains the value as a member | |
348 | return node['_M_data'] | |
349 | elif member == '_M_storage': | |
350 | # C++11 implementation, node stores value in __aligned_membuf | |
2dbe56bd JW |
351 | valtype = node.type.template_argument(0) |
352 | return get_value_from_aligned_membuf(node['_M_storage'], valtype) | |
cc7f3d0e JW |
353 | except: |
354 | pass | |
355 | raise ValueError("Unsupported implementation for %s" % str(node.type)) | |
356 | ||
41850419 TT |
357 | class StdListPrinter: |
358 | "Print a std::list" | |
359 | ||
cdd1ba62 | 360 | class _iterator(Iterator): |
41850419 TT |
361 | def __init__(self, nodetype, head): |
362 | self.nodetype = nodetype | |
363 | self.base = head['_M_next'] | |
364 | self.head = head.address | |
365 | self.count = 0 | |
366 | ||
367 | def __iter__(self): | |
368 | return self | |
369 | ||
cdd1ba62 | 370 | def __next__(self): |
41850419 TT |
371 | if self.base == self.head: |
372 | raise StopIteration | |
373 | elt = self.base.cast(self.nodetype).dereference() | |
374 | self.base = elt['_M_next'] | |
375 | count = self.count | |
376 | self.count = self.count + 1 | |
cc7f3d0e JW |
377 | val = get_value_from_list_node(elt) |
378 | return ('[%d]' % count, val) | |
41850419 | 379 | |
cd0961a5 | 380 | def __init__(self, typename, val): |
f9a27859 | 381 | self.typename = strip_versioned_namespace(typename) |
41850419 TT |
382 | self.val = val |
383 | ||
384 | def children(self): | |
9d50a6a7 | 385 | nodetype = lookup_node_type('_List_node', self.val.type).pointer() |
41850419 TT |
386 | return self._iterator(nodetype, self.val['_M_impl']['_M_node']) |
387 | ||
388 | def to_string(self): | |
9d50a6a7 JW |
389 | headnode = self.val['_M_impl']['_M_node'] |
390 | if headnode['_M_next'] == headnode.address: | |
cd0961a5 PM |
391 | return 'empty %s' % (self.typename) |
392 | return '%s' % (self.typename) | |
41850419 | 393 | |
fe6bd21a | 394 | class NodeIteratorPrinter: |
9d50a6a7 | 395 | def __init__(self, typename, val, contname, nodename): |
41850419 | 396 | self.val = val |
cd0961a5 | 397 | self.typename = typename |
fe6bd21a | 398 | self.contname = contname |
9d50a6a7 | 399 | self.nodetype = lookup_node_type(nodename, val.type) |
41850419 TT |
400 | |
401 | def to_string(self): | |
bdfc9f5c | 402 | if not self.val['_M_node']: |
fe6bd21a | 403 | return 'non-dereferenceable iterator for std::%s' % (self.contname) |
9d50a6a7 | 404 | node = self.val['_M_node'].cast(self.nodetype.pointer()).dereference() |
50a8a941 | 405 | return str(get_value_from_list_node(node)) |
41850419 | 406 | |
fe6bd21a FD |
407 | class StdListIteratorPrinter(NodeIteratorPrinter): |
408 | "Print std::list::iterator" | |
409 | ||
410 | def __init__(self, typename, val): | |
9d50a6a7 | 411 | NodeIteratorPrinter.__init__(self, typename, val, 'list', '_List_node') |
fe6bd21a FD |
412 | |
413 | class StdFwdListIteratorPrinter(NodeIteratorPrinter): | |
414 | "Print std::forward_list::iterator" | |
415 | ||
416 | def __init__(self, typename, val): | |
9d50a6a7 JW |
417 | NodeIteratorPrinter.__init__(self, typename, val, 'forward_list', |
418 | '_Fwd_list_node') | |
fe6bd21a | 419 | |
41850419 TT |
420 | class StdSlistPrinter: |
421 | "Print a __gnu_cxx::slist" | |
422 | ||
cdd1ba62 | 423 | class _iterator(Iterator): |
41850419 TT |
424 | def __init__(self, nodetype, head): |
425 | self.nodetype = nodetype | |
426 | self.base = head['_M_head']['_M_next'] | |
427 | self.count = 0 | |
428 | ||
429 | def __iter__(self): | |
430 | return self | |
431 | ||
cdd1ba62 | 432 | def __next__(self): |
41850419 TT |
433 | if self.base == 0: |
434 | raise StopIteration | |
435 | elt = self.base.cast(self.nodetype).dereference() | |
436 | self.base = elt['_M_next'] | |
437 | count = self.count | |
438 | self.count = self.count + 1 | |
439 | return ('[%d]' % count, elt['_M_data']) | |
440 | ||
d63c53cc | 441 | def __init__(self, typename, val): |
41850419 TT |
442 | self.val = val |
443 | ||
444 | def children(self): | |
9d50a6a7 JW |
445 | nodetype = lookup_node_type('__gnu_cxx::_Slist_node', self.val.type) |
446 | return self._iterator(nodetype.pointer(), self.val) | |
41850419 TT |
447 | |
448 | def to_string(self): | |
449 | if self.val['_M_head']['_M_next'] == 0: | |
450 | return 'empty __gnu_cxx::slist' | |
451 | return '__gnu_cxx::slist' | |
452 | ||
453 | class StdSlistIteratorPrinter: | |
454 | "Print __gnu_cxx::slist::iterator" | |
455 | ||
d63c53cc | 456 | def __init__(self, typename, val): |
41850419 TT |
457 | self.val = val |
458 | ||
459 | def to_string(self): | |
bdfc9f5c JW |
460 | if not self.val['_M_node']: |
461 | return 'non-dereferenceable iterator for __gnu_cxx::slist' | |
9d50a6a7 | 462 | nodetype = lookup_node_type('__gnu_cxx::_Slist_node', self.val.type).pointer() |
50a8a941 | 463 | return str(self.val['_M_node'].cast(nodetype).dereference()['_M_data']) |
41850419 TT |
464 | |
465 | class StdVectorPrinter: | |
466 | "Print a std::vector" | |
467 | ||
cdd1ba62 | 468 | class _iterator(Iterator): |
7bf7b578 CM |
469 | def __init__ (self, start, finish, bitvec): |
470 | self.bitvec = bitvec | |
471 | if bitvec: | |
472 | self.item = start['_M_p'] | |
6f00ccba | 473 | self.so = 0 |
7bf7b578 CM |
474 | self.finish = finish['_M_p'] |
475 | self.fo = finish['_M_offset'] | |
476 | itype = self.item.dereference().type | |
477 | self.isize = 8 * itype.sizeof | |
478 | else: | |
479 | self.item = start | |
480 | self.finish = finish | |
41850419 TT |
481 | self.count = 0 |
482 | ||
483 | def __iter__(self): | |
484 | return self | |
485 | ||
cdd1ba62 | 486 | def __next__(self): |
41850419 TT |
487 | count = self.count |
488 | self.count = self.count + 1 | |
7bf7b578 CM |
489 | if self.bitvec: |
490 | if self.item == self.finish and self.so >= self.fo: | |
491 | raise StopIteration | |
36d0dada | 492 | elt = bool(self.item.dereference() & (1 << self.so)) |
7bf7b578 CM |
493 | self.so = self.so + 1 |
494 | if self.so >= self.isize: | |
495 | self.item = self.item + 1 | |
496 | self.so = 0 | |
36d0dada | 497 | return ('[%d]' % count, elt) |
7bf7b578 CM |
498 | else: |
499 | if self.item == self.finish: | |
500 | raise StopIteration | |
501 | elt = self.item.dereference() | |
502 | self.item = self.item + 1 | |
503 | return ('[%d]' % count, elt) | |
41850419 | 504 | |
cd0961a5 | 505 | def __init__(self, typename, val): |
f9a27859 | 506 | self.typename = strip_versioned_namespace(typename) |
41850419 | 507 | self.val = val |
36d0dada | 508 | self.is_bool = val.type.template_argument(0).code == gdb.TYPE_CODE_BOOL |
41850419 TT |
509 | |
510 | def children(self): | |
511 | return self._iterator(self.val['_M_impl']['_M_start'], | |
7bf7b578 CM |
512 | self.val['_M_impl']['_M_finish'], |
513 | self.is_bool) | |
41850419 TT |
514 | |
515 | def to_string(self): | |
516 | start = self.val['_M_impl']['_M_start'] | |
517 | finish = self.val['_M_impl']['_M_finish'] | |
518 | end = self.val['_M_impl']['_M_end_of_storage'] | |
7bf7b578 CM |
519 | if self.is_bool: |
520 | start = self.val['_M_impl']['_M_start']['_M_p'] | |
7bf7b578 CM |
521 | finish = self.val['_M_impl']['_M_finish']['_M_p'] |
522 | fo = self.val['_M_impl']['_M_finish']['_M_offset'] | |
523 | itype = start.dereference().type | |
524 | bl = 8 * itype.sizeof | |
6f00ccba | 525 | length = bl * (finish - start) + fo |
7bf7b578 CM |
526 | capacity = bl * (end - start) |
527 | return ('%s<bool> of length %d, capacity %d' | |
528 | % (self.typename, int (length), int (capacity))) | |
529 | else: | |
530 | return ('%s of length %d, capacity %d' | |
531 | % (self.typename, int (finish - start), int (end - start))) | |
41850419 TT |
532 | |
533 | def display_hint(self): | |
534 | return 'array' | |
535 | ||
536 | class StdVectorIteratorPrinter: | |
537 | "Print std::vector::iterator" | |
538 | ||
d63c53cc | 539 | def __init__(self, typename, val): |
41850419 TT |
540 | self.val = val |
541 | ||
542 | def to_string(self): | |
bdfc9f5c JW |
543 | if not self.val['_M_current']: |
544 | return 'non-dereferenceable iterator for std::vector' | |
50a8a941 | 545 | return str(self.val['_M_current'].dereference()) |
41850419 | 546 | |
39836f83 MW |
547 | class StdBitIteratorPrinter: |
548 | "Print std::vector<bool>'s _Bit_iterator and _Bit_const_iterator" | |
549 | ||
550 | def __init__(self, typename, val): | |
551 | self.val = val | |
552 | ||
553 | def to_string(self): | |
554 | if not self.val['_M_p']: | |
555 | return 'non-dereferenceable iterator for std::vector<bool>' | |
556 | return bool(self.val['_M_p'].dereference() & (1 << self.val['_M_offset'])) | |
557 | ||
558 | class StdBitReferencePrinter: | |
c883d1dc | 559 | "Print std::vector<bool>::reference" |
39836f83 MW |
560 | |
561 | def __init__(self, typename, val): | |
562 | self.val = val | |
563 | ||
564 | def to_string(self): | |
565 | if not self.val['_M_p']: | |
c883d1dc | 566 | return 'invalid std::vector<bool>::reference' |
39836f83 | 567 | return bool(self.val['_M_p'].dereference() & (self.val['_M_mask'])) |
36d0dada | 568 | |
8345c8e4 PM |
569 | class StdTuplePrinter: |
570 | "Print a std::tuple" | |
571 | ||
cdd1ba62 | 572 | class _iterator(Iterator): |
478490f6 JW |
573 | @staticmethod |
574 | def _is_nonempty_tuple (nodes): | |
575 | if len (nodes) == 2: | |
576 | if is_specialization_of (nodes[1].type, '__tuple_base'): | |
577 | return True | |
578 | elif len (nodes) == 1: | |
579 | return True | |
580 | elif len (nodes) == 0: | |
581 | return False | |
582 | raise ValueError("Top of tuple tree does not consist of a single node.") | |
583 | ||
8345c8e4 PM |
584 | def __init__ (self, head): |
585 | self.head = head | |
586 | ||
587 | # Set the base class as the initial head of the | |
588 | # tuple. | |
589 | nodes = self.head.type.fields () | |
478490f6 | 590 | if self._is_nonempty_tuple (nodes): |
9dacb44b JW |
591 | # Set the actual head to the first pair. |
592 | self.head = self.head.cast (nodes[0].type) | |
8345c8e4 PM |
593 | self.count = 0 |
594 | ||
595 | def __iter__ (self): | |
596 | return self | |
597 | ||
cdd1ba62 | 598 | def __next__ (self): |
8345c8e4 | 599 | # Check for further recursions in the inheritance tree. |
deaa1ccb JW |
600 | # For a GCC 5+ tuple self.head is None after visiting all nodes: |
601 | if not self.head: | |
602 | raise StopIteration | |
603 | nodes = self.head.type.fields () | |
604 | # For a GCC 4.x tuple there is a final node with no fields: | |
8345c8e4 PM |
605 | if len (nodes) == 0: |
606 | raise StopIteration | |
607 | # Check that this iteration has an expected structure. | |
deaa1ccb | 608 | if len (nodes) > 2: |
47059228 | 609 | raise ValueError("Cannot parse more than 2 nodes in a tuple tree.") |
8345c8e4 | 610 | |
deaa1ccb JW |
611 | if len (nodes) == 1: |
612 | # This is the last node of a GCC 5+ std::tuple. | |
613 | impl = self.head.cast (nodes[0].type) | |
614 | self.head = None | |
615 | else: | |
616 | # Either a node before the last node, or the last node of | |
617 | # a GCC 4.x tuple (which has an empty parent). | |
618 | ||
619 | # - Left node is the next recursion parent. | |
620 | # - Right node is the actual class contained in the tuple. | |
8345c8e4 | 621 | |
deaa1ccb JW |
622 | # Process right node. |
623 | impl = self.head.cast (nodes[1].type) | |
624 | ||
625 | # Process left node and set it as head. | |
626 | self.head = self.head.cast (nodes[0].type) | |
8345c8e4 | 627 | |
8345c8e4 PM |
628 | self.count = self.count + 1 |
629 | ||
630 | # Finally, check the implementation. If it is | |
631 | # wrapped in _M_head_impl return that, otherwise return | |
632 | # the value "as is". | |
633 | fields = impl.type.fields () | |
634 | if len (fields) < 1 or fields[0].name != "_M_head_impl": | |
07568376 | 635 | return ('[%d]' % (self.count - 1), impl) |
8345c8e4 | 636 | else: |
07568376 | 637 | return ('[%d]' % (self.count - 1), impl['_M_head_impl']) |
8345c8e4 PM |
638 | |
639 | def __init__ (self, typename, val): | |
f9a27859 | 640 | self.typename = strip_versioned_namespace(typename) |
8345c8e4 PM |
641 | self.val = val; |
642 | ||
643 | def children (self): | |
644 | return self._iterator (self.val) | |
645 | ||
646 | def to_string (self): | |
9dacb44b JW |
647 | if len (self.val.type.fields ()) == 0: |
648 | return 'empty %s' % (self.typename) | |
8345c8e4 PM |
649 | return '%s containing' % (self.typename) |
650 | ||
41850419 TT |
651 | class StdStackOrQueuePrinter: |
652 | "Print a std::stack or std::queue" | |
653 | ||
654 | def __init__ (self, typename, val): | |
f9a27859 | 655 | self.typename = strip_versioned_namespace(typename) |
41850419 TT |
656 | self.visualizer = gdb.default_visualizer(val['c']) |
657 | ||
658 | def children (self): | |
659 | return self.visualizer.children() | |
660 | ||
661 | def to_string (self): | |
662 | return '%s wrapping: %s' % (self.typename, | |
663 | self.visualizer.to_string()) | |
664 | ||
665 | def display_hint (self): | |
666 | if hasattr (self.visualizer, 'display_hint'): | |
667 | return self.visualizer.display_hint () | |
668 | return None | |
669 | ||
cdd1ba62 | 670 | class RbtreeIterator(Iterator): |
7224c6a9 JW |
671 | """ |
672 | Turn an RB-tree-based container (std::map, std::set etc.) into | |
673 | a Python iterable object. | |
674 | """ | |
675 | ||
41850419 TT |
676 | def __init__(self, rbtree): |
677 | self.size = rbtree['_M_t']['_M_impl']['_M_node_count'] | |
678 | self.node = rbtree['_M_t']['_M_impl']['_M_header']['_M_left'] | |
679 | self.count = 0 | |
680 | ||
681 | def __iter__(self): | |
682 | return self | |
683 | ||
684 | def __len__(self): | |
685 | return int (self.size) | |
686 | ||
cdd1ba62 | 687 | def __next__(self): |
41850419 TT |
688 | if self.count == self.size: |
689 | raise StopIteration | |
690 | result = self.node | |
691 | self.count = self.count + 1 | |
692 | if self.count < self.size: | |
693 | # Compute the next node. | |
694 | node = self.node | |
695 | if node.dereference()['_M_right']: | |
696 | node = node.dereference()['_M_right'] | |
697 | while node.dereference()['_M_left']: | |
698 | node = node.dereference()['_M_left'] | |
699 | else: | |
700 | parent = node.dereference()['_M_parent'] | |
701 | while node == parent.dereference()['_M_right']: | |
702 | node = parent | |
703 | parent = parent.dereference()['_M_parent'] | |
704 | if node.dereference()['_M_right'] != parent: | |
705 | node = parent | |
706 | self.node = node | |
707 | return result | |
708 | ||
047f1cec JW |
709 | def get_value_from_Rb_tree_node(node): |
710 | """Returns the value held in an _Rb_tree_node<_Val>""" | |
711 | try: | |
712 | member = node.type.fields()[1].name | |
713 | if member == '_M_value_field': | |
714 | # C++03 implementation, node contains the value as a member | |
715 | return node['_M_value_field'] | |
716 | elif member == '_M_storage': | |
cc7f3d0e | 717 | # C++11 implementation, node stores value in __aligned_membuf |
2dbe56bd JW |
718 | valtype = node.type.template_argument(0) |
719 | return get_value_from_aligned_membuf(node['_M_storage'], valtype) | |
047f1cec JW |
720 | except: |
721 | pass | |
47059228 | 722 | raise ValueError("Unsupported implementation for %s" % str(node.type)) |
047f1cec | 723 | |
41850419 TT |
724 | # This is a pretty printer for std::_Rb_tree_iterator (which is |
725 | # std::map::iterator), and has nothing to do with the RbtreeIterator | |
726 | # class above. | |
727 | class StdRbtreeIteratorPrinter: | |
7224c6a9 | 728 | "Print std::map::iterator, std::set::iterator, etc." |
41850419 | 729 | |
d63c53cc | 730 | def __init__ (self, typename, val): |
41850419 | 731 | self.val = val |
9d50a6a7 JW |
732 | nodetype = lookup_node_type('_Rb_tree_node', self.val.type) |
733 | self.link_type = nodetype.pointer() | |
41850419 TT |
734 | |
735 | def to_string (self): | |
bdfc9f5c JW |
736 | if not self.val['_M_node']: |
737 | return 'non-dereferenceable iterator for associative container' | |
2097b5b0 | 738 | node = self.val['_M_node'].cast(self.link_type).dereference() |
50a8a941 | 739 | return str(get_value_from_Rb_tree_node(node)) |
41850419 | 740 | |
cd0961a5 PM |
741 | class StdDebugIteratorPrinter: |
742 | "Print a debug enabled version of an iterator" | |
743 | ||
d63c53cc | 744 | def __init__ (self, typename, val): |
cd0961a5 PM |
745 | self.val = val |
746 | ||
747 | # Just strip away the encapsulating __gnu_debug::_Safe_iterator | |
748 | # and return the wrapped iterator value. | |
749 | def to_string (self): | |
3c760f4a | 750 | base_type = gdb.lookup_type('__gnu_debug::_Safe_iterator_base') |
fe6bd21a | 751 | itype = self.val.type.template_argument(0) |
3c760f4a | 752 | safe_seq = self.val.cast(base_type)['_M_sequence'] |
fe6bd21a FD |
753 | if not safe_seq: |
754 | return str(self.val.cast(itype)) | |
755 | if self.val['_M_version'] != safe_seq['_M_version']: | |
3c760f4a | 756 | return "invalid iterator" |
50a8a941 | 757 | return str(self.val.cast(itype)) |
41850419 | 758 | |
d33c00e1 JW |
759 | def num_elements(num): |
760 | """Return either "1 element" or "N elements" depending on the argument.""" | |
761 | return '1 element' if num == 1 else '%d elements' % num | |
762 | ||
41850419 TT |
763 | class StdMapPrinter: |
764 | "Print a std::map or std::multimap" | |
765 | ||
766 | # Turn an RbtreeIterator into a pretty-print iterator. | |
cdd1ba62 | 767 | class _iter(Iterator): |
41850419 TT |
768 | def __init__(self, rbiter, type): |
769 | self.rbiter = rbiter | |
770 | self.count = 0 | |
771 | self.type = type | |
772 | ||
773 | def __iter__(self): | |
774 | return self | |
775 | ||
cdd1ba62 | 776 | def __next__(self): |
41850419 | 777 | if self.count % 2 == 0: |
cdd1ba62 | 778 | n = next(self.rbiter) |
047f1cec JW |
779 | n = n.cast(self.type).dereference() |
780 | n = get_value_from_Rb_tree_node(n) | |
41850419 TT |
781 | self.pair = n |
782 | item = n['first'] | |
783 | else: | |
784 | item = self.pair['second'] | |
785 | result = ('[%d]' % self.count, item) | |
786 | self.count = self.count + 1 | |
787 | return result | |
788 | ||
789 | def __init__ (self, typename, val): | |
f9a27859 | 790 | self.typename = strip_versioned_namespace(typename) |
41850419 | 791 | self.val = val |
41850419 TT |
792 | |
793 | def to_string (self): | |
d33c00e1 JW |
794 | return '%s with %s' % (self.typename, |
795 | num_elements(len(RbtreeIterator (self.val)))) | |
41850419 TT |
796 | |
797 | def children (self): | |
9d50a6a7 | 798 | node = lookup_node_type('_Rb_tree_node', self.val.type).pointer() |
3efe2bf7 | 799 | return self._iter (RbtreeIterator (self.val), node) |
41850419 TT |
800 | |
801 | def display_hint (self): | |
802 | return 'map' | |
803 | ||
804 | class StdSetPrinter: | |
805 | "Print a std::set or std::multiset" | |
806 | ||
807 | # Turn an RbtreeIterator into a pretty-print iterator. | |
cdd1ba62 | 808 | class _iter(Iterator): |
41850419 TT |
809 | def __init__(self, rbiter, type): |
810 | self.rbiter = rbiter | |
811 | self.count = 0 | |
812 | self.type = type | |
813 | ||
814 | def __iter__(self): | |
815 | return self | |
816 | ||
cdd1ba62 SB |
817 | def __next__(self): |
818 | item = next(self.rbiter) | |
047f1cec JW |
819 | item = item.cast(self.type).dereference() |
820 | item = get_value_from_Rb_tree_node(item) | |
41850419 TT |
821 | # FIXME: this is weird ... what to do? |
822 | # Maybe a 'set' display hint? | |
823 | result = ('[%d]' % self.count, item) | |
824 | self.count = self.count + 1 | |
825 | return result | |
826 | ||
827 | def __init__ (self, typename, val): | |
f9a27859 | 828 | self.typename = strip_versioned_namespace(typename) |
41850419 | 829 | self.val = val |
41850419 TT |
830 | |
831 | def to_string (self): | |
d33c00e1 JW |
832 | return '%s with %s' % (self.typename, |
833 | num_elements(len(RbtreeIterator (self.val)))) | |
41850419 TT |
834 | |
835 | def children (self): | |
9d50a6a7 | 836 | node = lookup_node_type('_Rb_tree_node', self.val.type).pointer() |
3efe2bf7 | 837 | return self._iter (RbtreeIterator (self.val), node) |
41850419 TT |
838 | |
839 | class StdBitsetPrinter: | |
840 | "Print a std::bitset" | |
841 | ||
cd0961a5 | 842 | def __init__(self, typename, val): |
f9a27859 | 843 | self.typename = strip_versioned_namespace(typename) |
41850419 TT |
844 | self.val = val |
845 | ||
846 | def to_string (self): | |
847 | # If template_argument handled values, we could print the | |
848 | # size. Or we could use a regexp on the type. | |
cd0961a5 | 849 | return '%s' % (self.typename) |
41850419 TT |
850 | |
851 | def children (self): | |
14a9206d MS |
852 | try: |
853 | # An empty bitset may not have any members which will | |
854 | # result in an exception being thrown. | |
855 | words = self.val['_M_w'] | |
856 | except: | |
857 | return [] | |
858 | ||
41850419 TT |
859 | wtype = words.type |
860 | ||
861 | # The _M_w member can be either an unsigned long, or an | |
862 | # array. This depends on the template specialization used. | |
863 | # If it is a single long, convert to a single element list. | |
864 | if wtype.code == gdb.TYPE_CODE_ARRAY: | |
865 | tsize = wtype.target ().sizeof | |
866 | else: | |
867 | words = [words] | |
14a9206d | 868 | tsize = wtype.sizeof |
41850419 TT |
869 | |
870 | nwords = wtype.sizeof / tsize | |
871 | result = [] | |
872 | byte = 0 | |
873 | while byte < nwords: | |
874 | w = words[byte] | |
875 | bit = 0 | |
876 | while w != 0: | |
877 | if (w & 1) != 0: | |
878 | # Another spot where we could use 'set'? | |
879 | result.append(('[%d]' % (byte * tsize * 8 + bit), 1)) | |
880 | bit = bit + 1 | |
881 | w = w >> 1 | |
882 | byte = byte + 1 | |
883 | return result | |
884 | ||
885 | class StdDequePrinter: | |
886 | "Print a std::deque" | |
887 | ||
cdd1ba62 | 888 | class _iter(Iterator): |
41850419 TT |
889 | def __init__(self, node, start, end, last, buffer_size): |
890 | self.node = node | |
891 | self.p = start | |
892 | self.end = end | |
893 | self.last = last | |
894 | self.buffer_size = buffer_size | |
895 | self.count = 0 | |
896 | ||
897 | def __iter__(self): | |
898 | return self | |
899 | ||
cdd1ba62 | 900 | def __next__(self): |
41850419 TT |
901 | if self.p == self.last: |
902 | raise StopIteration | |
903 | ||
904 | result = ('[%d]' % self.count, self.p.dereference()) | |
905 | self.count = self.count + 1 | |
906 | ||
907 | # Advance the 'cur' pointer. | |
908 | self.p = self.p + 1 | |
909 | if self.p == self.end: | |
910 | # If we got to the end of this bucket, move to the | |
911 | # next bucket. | |
912 | self.node = self.node + 1 | |
913 | self.p = self.node[0] | |
914 | self.end = self.p + self.buffer_size | |
915 | ||
916 | return result | |
917 | ||
cd0961a5 | 918 | def __init__(self, typename, val): |
f9a27859 | 919 | self.typename = strip_versioned_namespace(typename) |
41850419 TT |
920 | self.val = val |
921 | self.elttype = val.type.template_argument(0) | |
922 | size = self.elttype.sizeof | |
923 | if size < 512: | |
924 | self.buffer_size = int (512 / size) | |
925 | else: | |
926 | self.buffer_size = 1 | |
927 | ||
928 | def to_string(self): | |
929 | start = self.val['_M_impl']['_M_start'] | |
930 | end = self.val['_M_impl']['_M_finish'] | |
931 | ||
932 | delta_n = end['_M_node'] - start['_M_node'] - 1 | |
933 | delta_s = start['_M_last'] - start['_M_cur'] | |
934 | delta_e = end['_M_cur'] - end['_M_first'] | |
935 | ||
936 | size = self.buffer_size * delta_n + delta_s + delta_e | |
937 | ||
d33c00e1 | 938 | return '%s with %s' % (self.typename, num_elements(long(size))) |
41850419 TT |
939 | |
940 | def children(self): | |
941 | start = self.val['_M_impl']['_M_start'] | |
942 | end = self.val['_M_impl']['_M_finish'] | |
943 | return self._iter(start['_M_node'], start['_M_cur'], start['_M_last'], | |
944 | end['_M_cur'], self.buffer_size) | |
945 | ||
946 | def display_hint (self): | |
947 | return 'array' | |
948 | ||
949 | class StdDequeIteratorPrinter: | |
950 | "Print std::deque::iterator" | |
951 | ||
d63c53cc | 952 | def __init__(self, typename, val): |
41850419 TT |
953 | self.val = val |
954 | ||
955 | def to_string(self): | |
bdfc9f5c JW |
956 | if not self.val['_M_cur']: |
957 | return 'non-dereferenceable iterator for std::deque' | |
50a8a941 | 958 | return str(self.val['_M_cur'].dereference()) |
41850419 TT |
959 | |
960 | class StdStringPrinter: | |
961 | "Print a std::basic_string of some kind" | |
962 | ||
d63c53cc | 963 | def __init__(self, typename, val): |
41850419 | 964 | self.val = val |
34a2b755 | 965 | self.new_string = typename.find("::__cxx11::basic_string") != -1 |
41850419 TT |
966 | |
967 | def to_string(self): | |
271167f1 PM |
968 | # Make sure &string works, too. |
969 | type = self.val.type | |
970 | if type.code == gdb.TYPE_CODE_REF: | |
971 | type = type.target () | |
972 | ||
973 | # Calculate the length of the string so that to_string returns | |
974 | # the string according to length, not according to first null | |
975 | # encountered. | |
976 | ptr = self.val ['_M_dataplus']['_M_p'] | |
34a2b755 JW |
977 | if self.new_string: |
978 | length = self.val['_M_string_length'] | |
979 | # https://sourceware.org/bugzilla/show_bug.cgi?id=17728 | |
980 | ptr = ptr.cast(ptr.type.strip_typedefs()) | |
981 | else: | |
982 | realtype = type.unqualified ().strip_typedefs () | |
983 | reptype = gdb.lookup_type (str (realtype) + '::_Rep').pointer () | |
984 | header = ptr.cast(reptype) - 1 | |
985 | length = header.dereference ()['_M_length'] | |
708f539d | 986 | if hasattr(ptr, "lazy_string"): |
34a2b755 JW |
987 | return ptr.lazy_string (length = length) |
988 | return ptr.string (length = length) | |
41850419 TT |
989 | |
990 | def display_hint (self): | |
991 | return 'string' | |
992 | ||
93257ed6 PF |
993 | def access_streambuf_ptrs(streambuf): |
994 | "Access the streambuf put area pointers" | |
995 | pbase = streambuf['_M_out_beg'] | |
996 | pptr = streambuf['_M_out_cur'] | |
997 | egptr = streambuf['_M_in_end'] | |
998 | return pbase, pptr, egptr | |
999 | ||
1000 | class StdStringBufPrinter: | |
1001 | "Print a std::basic_stringbuf" | |
1002 | ||
1003 | def __init__(self, _, val): | |
1004 | self.val = val | |
1005 | ||
1006 | def to_string(self): | |
1007 | (pbase, pptr, egptr) = access_streambuf_ptrs(self.val) | |
1008 | # Logic from basic_stringbuf::_M_high_mark() | |
1009 | if pptr: | |
1010 | if not egptr or pptr > egptr: | |
1011 | return pbase.string(length = pptr - pbase) | |
1012 | else: | |
1013 | return pbase.string(length = egptr - pbase) | |
1014 | return self.val['_M_string'] | |
1015 | ||
1016 | def display_hint(self): | |
1017 | return 'string' | |
1018 | ||
1019 | class StdStringStreamPrinter: | |
1020 | "Print a std::basic_stringstream" | |
1021 | ||
1022 | def __init__(self, typename, val): | |
1023 | self.val = val | |
1024 | self.typename = typename | |
1025 | ||
1026 | # Check if the stream was redirected: | |
1027 | # This is essentially: val['_M_streambuf'] == val['_M_stringbuf'].address | |
1028 | # However, GDB can't resolve the virtual inheritance, so we do that manually | |
1029 | basetype = [f.type for f in val.type.fields() if f.is_base_class][0] | |
1030 | gdb.set_convenience_variable('__stream', val.cast(basetype).address) | |
1031 | self.streambuf = gdb.parse_and_eval('$__stream->rdbuf()') | |
1032 | self.was_redirected = self.streambuf != val['_M_stringbuf'].address | |
1033 | ||
1034 | def to_string(self): | |
1035 | if self.was_redirected: | |
1036 | return "%s redirected to %s" % (self.typename, self.streambuf.dereference()) | |
1037 | return self.val['_M_stringbuf'] | |
1038 | ||
1039 | def display_hint(self): | |
1040 | if self.was_redirected: | |
1041 | return None | |
1042 | return 'string' | |
1043 | ||
cdd1ba62 | 1044 | class Tr1HashtableIterator(Iterator): |
9d50a6a7 JW |
1045 | def __init__ (self, hashtable): |
1046 | self.buckets = hashtable['_M_buckets'] | |
4722d005 | 1047 | self.bucket = 0 |
9d50a6a7 JW |
1048 | self.bucket_count = hashtable['_M_bucket_count'] |
1049 | self.node_type = find_type(hashtable.type, '_Node').pointer() | |
4722d005 FD |
1050 | self.node = 0 |
1051 | while self.bucket != self.bucket_count: | |
1052 | self.node = self.buckets[self.bucket] | |
1053 | if self.node: | |
1054 | break | |
14a9206d | 1055 | self.bucket = self.bucket + 1 |
41850419 TT |
1056 | |
1057 | def __iter__ (self): | |
1058 | return self | |
1059 | ||
cdd1ba62 | 1060 | def __next__ (self): |
d25b1e3a | 1061 | if self.node == 0: |
41850419 | 1062 | raise StopIteration |
d25b1e3a TT |
1063 | node = self.node.cast(self.node_type) |
1064 | result = node.dereference()['_M_v'] | |
4722d005 FD |
1065 | self.node = node.dereference()['_M_next']; |
1066 | if self.node == 0: | |
1067 | self.bucket = self.bucket + 1 | |
1068 | while self.bucket != self.bucket_count: | |
1069 | self.node = self.buckets[self.bucket] | |
1070 | if self.node: | |
1071 | break | |
1072 | self.bucket = self.bucket + 1 | |
41850419 TT |
1073 | return result |
1074 | ||
cdd1ba62 | 1075 | class StdHashtableIterator(Iterator): |
9d50a6a7 JW |
1076 | def __init__(self, hashtable): |
1077 | self.node = hashtable['_M_before_begin']['_M_nxt'] | |
1078 | valtype = hashtable.type.template_argument(1) | |
1079 | cached = hashtable.type.template_argument(9).template_argument(0) | |
1080 | node_type = lookup_templ_spec('std::__detail::_Hash_node', str(valtype), | |
1081 | 'true' if cached else 'false') | |
1082 | self.node_type = node_type.pointer() | |
4722d005 FD |
1083 | |
1084 | def __iter__(self): | |
1085 | return self | |
1086 | ||
cdd1ba62 | 1087 | def __next__(self): |
4722d005 FD |
1088 | if self.node == 0: |
1089 | raise StopIteration | |
1090 | elt = self.node.cast(self.node_type).dereference() | |
1091 | self.node = elt['_M_nxt'] | |
1092 | valptr = elt['_M_storage'].address | |
1093 | valptr = valptr.cast(elt.type.template_argument(0).pointer()) | |
1094 | return valptr.dereference() | |
1095 | ||
41850419 | 1096 | class Tr1UnorderedSetPrinter: |
9d50a6a7 | 1097 | "Print a std::unordered_set or tr1::unordered_set" |
41850419 TT |
1098 | |
1099 | def __init__ (self, typename, val): | |
f9a27859 | 1100 | self.typename = strip_versioned_namespace(typename) |
41850419 TT |
1101 | self.val = val |
1102 | ||
937b190d JW |
1103 | def hashtable (self): |
1104 | if self.typename.startswith('std::tr1'): | |
1105 | return self.val | |
1106 | return self.val['_M_h'] | |
1107 | ||
41850419 | 1108 | def to_string (self): |
d33c00e1 JW |
1109 | count = self.hashtable()['_M_element_count'] |
1110 | return '%s with %s' % (self.typename, num_elements(count)) | |
41850419 TT |
1111 | |
1112 | @staticmethod | |
1113 | def format_count (i): | |
1114 | return '[%d]' % i | |
1115 | ||
1116 | def children (self): | |
cdd1ba62 | 1117 | counter = imap (self.format_count, itertools.count()) |
4722d005 | 1118 | if self.typename.startswith('std::tr1'): |
cdd1ba62 SB |
1119 | return izip (counter, Tr1HashtableIterator (self.hashtable())) |
1120 | return izip (counter, StdHashtableIterator (self.hashtable())) | |
41850419 TT |
1121 | |
1122 | class Tr1UnorderedMapPrinter: | |
9d50a6a7 | 1123 | "Print a std::unordered_map or tr1::unordered_map" |
41850419 TT |
1124 | |
1125 | def __init__ (self, typename, val): | |
f9a27859 | 1126 | self.typename = strip_versioned_namespace(typename) |
41850419 TT |
1127 | self.val = val |
1128 | ||
937b190d JW |
1129 | def hashtable (self): |
1130 | if self.typename.startswith('std::tr1'): | |
1131 | return self.val | |
1132 | return self.val['_M_h'] | |
1133 | ||
41850419 | 1134 | def to_string (self): |
d33c00e1 JW |
1135 | count = self.hashtable()['_M_element_count'] |
1136 | return '%s with %s' % (self.typename, num_elements(count)) | |
41850419 TT |
1137 | |
1138 | @staticmethod | |
1139 | def flatten (list): | |
1140 | for elt in list: | |
1141 | for i in elt: | |
1142 | yield i | |
1143 | ||
1144 | @staticmethod | |
1145 | def format_one (elt): | |
1146 | return (elt['first'], elt['second']) | |
1147 | ||
1148 | @staticmethod | |
1149 | def format_count (i): | |
1150 | return '[%d]' % i | |
1151 | ||
1152 | def children (self): | |
cdd1ba62 | 1153 | counter = imap (self.format_count, itertools.count()) |
41850419 | 1154 | # Map over the hash table and flatten the result. |
4722d005 | 1155 | if self.typename.startswith('std::tr1'): |
cdd1ba62 | 1156 | data = self.flatten (imap (self.format_one, Tr1HashtableIterator (self.hashtable()))) |
4722d005 | 1157 | # Zip the two iterators together. |
cdd1ba62 SB |
1158 | return izip (counter, data) |
1159 | data = self.flatten (imap (self.format_one, StdHashtableIterator (self.hashtable()))) | |
41850419 | 1160 | # Zip the two iterators together. |
cdd1ba62 | 1161 | return izip (counter, data) |
41850419 TT |
1162 | |
1163 | def display_hint (self): | |
1164 | return 'map' | |
1165 | ||
8dfb08ab JW |
1166 | class StdForwardListPrinter: |
1167 | "Print a std::forward_list" | |
1168 | ||
cdd1ba62 | 1169 | class _iterator(Iterator): |
8dfb08ab JW |
1170 | def __init__(self, nodetype, head): |
1171 | self.nodetype = nodetype | |
1172 | self.base = head['_M_next'] | |
1173 | self.count = 0 | |
1174 | ||
1175 | def __iter__(self): | |
1176 | return self | |
1177 | ||
cdd1ba62 | 1178 | def __next__(self): |
8dfb08ab JW |
1179 | if self.base == 0: |
1180 | raise StopIteration | |
1181 | elt = self.base.cast(self.nodetype).dereference() | |
1182 | self.base = elt['_M_next'] | |
1183 | count = self.count | |
1184 | self.count = self.count + 1 | |
073deae6 JW |
1185 | valptr = elt['_M_storage'].address |
1186 | valptr = valptr.cast(elt.type.template_argument(0).pointer()) | |
1187 | return ('[%d]' % count, valptr.dereference()) | |
8dfb08ab JW |
1188 | |
1189 | def __init__(self, typename, val): | |
1190 | self.val = val | |
f9a27859 | 1191 | self.typename = strip_versioned_namespace(typename) |
8dfb08ab JW |
1192 | |
1193 | def children(self): | |
9d50a6a7 | 1194 | nodetype = lookup_node_type('_Fwd_list_node', self.val.type).pointer() |
8dfb08ab JW |
1195 | return self._iterator(nodetype, self.val['_M_impl']['_M_head']) |
1196 | ||
1197 | def to_string(self): | |
1198 | if self.val['_M_impl']['_M_head']['_M_next'] == 0: | |
7224c6a9 JW |
1199 | return 'empty %s' % self.typename |
1200 | return '%s' % self.typename | |
8dfb08ab | 1201 | |
6f440cce JW |
1202 | class SingleObjContainerPrinter(object): |
1203 | "Base class for printers of containers of single objects" | |
1204 | ||
a0a1009a | 1205 | def __init__ (self, val, viz, hint = None): |
6f440cce JW |
1206 | self.contained_value = val |
1207 | self.visualizer = viz | |
a0a1009a | 1208 | self.hint = hint |
6f440cce JW |
1209 | |
1210 | def _recognize(self, type): | |
1211 | """Return TYPE as a string after applying type printers""" | |
faf7f829 JW |
1212 | global _use_type_printing |
1213 | if not _use_type_printing: | |
1214 | return str(type) | |
6f440cce JW |
1215 | return gdb.types.apply_type_recognizers(gdb.types.get_type_recognizers(), |
1216 | type) or str(type) | |
1217 | ||
3e5eda5b | 1218 | class _contained(Iterator): |
6f440cce JW |
1219 | def __init__ (self, val): |
1220 | self.val = val | |
1221 | ||
1222 | def __iter__ (self): | |
1223 | return self | |
1224 | ||
3e5eda5b | 1225 | def __next__(self): |
6f440cce JW |
1226 | if self.val is None: |
1227 | raise StopIteration | |
1228 | retval = self.val | |
1229 | self.val = None | |
1230 | return ('[contained value]', retval) | |
1231 | ||
1232 | def children (self): | |
1233 | if self.contained_value is None: | |
1234 | return self._contained (None) | |
1235 | if hasattr (self.visualizer, 'children'): | |
1236 | return self.visualizer.children () | |
1237 | return self._contained (self.contained_value) | |
1238 | ||
1239 | def display_hint (self): | |
1240 | # if contained value is a map we want to display in the same way | |
1241 | if hasattr (self.visualizer, 'children') and hasattr (self.visualizer, 'display_hint'): | |
1242 | return self.visualizer.display_hint () | |
a0a1009a | 1243 | return self.hint |
6f440cce | 1244 | |
dc2b372e JW |
1245 | def function_pointer_to_name(f): |
1246 | "Find the name of the function referred to by the gdb.Value f, " | |
1247 | " which should contain a function pointer from the program." | |
1248 | ||
1249 | # Turn the function pointer into an actual address. | |
1250 | # This is needed to unpack ppc64 function descriptors. | |
1251 | f = f.dereference().address | |
1252 | ||
1253 | if sys.version_info[0] == 2: | |
1254 | # Older versions of GDB need to use long for Python 2, | |
1255 | # because int(f) on 64-bit big-endian values raises a | |
1256 | # gdb.error saying "Cannot convert value to int." | |
1257 | f = long(f) | |
1258 | else: | |
1259 | f = int(f) | |
1260 | ||
1261 | try: | |
1262 | # If the function can't be found older versions of GDB raise a | |
1263 | # RuntimeError saying "Cannot locate object file for block." | |
1264 | return gdb.block_for_pc(f).function.name | |
1265 | except: | |
1266 | return None | |
1267 | ||
6f440cce | 1268 | class StdExpAnyPrinter(SingleObjContainerPrinter): |
00895372 | 1269 | "Print a std::any or std::experimental::any" |
6f440cce JW |
1270 | |
1271 | def __init__ (self, typename, val): | |
86397ed1 FD |
1272 | self.typename = strip_versioned_namespace(typename) |
1273 | self.typename = re.sub('^std::experimental::fundamentals_v\d::', 'std::experimental::', self.typename, 1) | |
6f440cce JW |
1274 | self.val = val |
1275 | self.contained_type = None | |
1276 | contained_value = None | |
1277 | visualizer = None | |
1278 | mgr = self.val['_M_manager'] | |
1279 | if mgr != 0: | |
dc2b372e | 1280 | func = function_pointer_to_name(mgr) |
6f440cce | 1281 | if not func: |
dc2b372e | 1282 | raise ValueError("Invalid function pointer in %s" % (self.typename)) |
11aa881f | 1283 | rx = r"""({0}::_Manager_\w+<.*>)::_S_manage\((enum )?{0}::_Op, (const {0}|{0} const) ?\*, (union )?{0}::_Arg ?\*\)""".format(typename) |
dc2b372e | 1284 | m = re.match(rx, func) |
6f440cce | 1285 | if not m: |
00895372 | 1286 | raise ValueError("Unknown manager function in %s" % self.typename) |
6f440cce | 1287 | |
f9a27859 | 1288 | mgrname = m.group(1) |
6f440cce | 1289 | # FIXME need to expand 'std::string' so that gdb.lookup_type works |
f9a27859 | 1290 | if 'std::string' in mgrname: |
3c54805d JW |
1291 | # This lookup for std::string might return the __cxx11 version, |
1292 | # but that's not necessarily the one used by the std::any | |
1293 | # manager function we're trying to find. | |
1294 | strings = {str(gdb.lookup_type('std::string').strip_typedefs())} | |
1295 | # So also consider all the other possible std::string types! | |
1296 | s = 'basic_string<char, std::char_traits<char>, std::allocator<char> >' | |
1297 | quals = ['std::', 'std::__cxx11::', 'std::' + _versioned_namespace] | |
1298 | strings |= {q+s for q in quals} # set of unique strings | |
1299 | mgrtypes = [] | |
1300 | for s in strings: | |
1301 | try: | |
1302 | x = re.sub("std::string(?!\w)", s, m.group(1)) | |
1303 | # The following lookup might raise gdb.error if the | |
1304 | # manager function was never instantiated for 's' in the | |
1305 | # program, because there will be no such type. | |
1306 | mgrtypes.append(gdb.lookup_type(x)) | |
1307 | except gdb.error: | |
1308 | pass | |
1309 | if len(mgrtypes) != 1: | |
1310 | # FIXME: this is unlikely in practice, but possible for | |
1311 | # programs that use both old and new string types with | |
1312 | # std::any in a single program. Can we do better? | |
1313 | # Maybe find the address of each type's _S_manage and | |
1314 | # compare to the address stored in _M_manager? | |
1315 | raise ValueError('Cannot uniquely determine std::string type used in std::any') | |
1316 | mgrtype = mgrtypes[0] | |
1317 | else: | |
1318 | mgrtype = gdb.lookup_type(mgrname) | |
6f440cce JW |
1319 | self.contained_type = mgrtype.template_argument(0) |
1320 | valptr = None | |
1321 | if '::_Manager_internal' in mgrname: | |
1322 | valptr = self.val['_M_storage']['_M_buffer'].address | |
1323 | elif '::_Manager_external' in mgrname: | |
1324 | valptr = self.val['_M_storage']['_M_ptr'] | |
6f440cce | 1325 | else: |
00895372 | 1326 | raise ValueError("Unknown manager function in %s" % self.typename) |
6f440cce JW |
1327 | contained_value = valptr.cast(self.contained_type.pointer()).dereference() |
1328 | visualizer = gdb.default_visualizer(contained_value) | |
1329 | super(StdExpAnyPrinter, self).__init__ (contained_value, visualizer) | |
1330 | ||
1331 | def to_string (self): | |
1332 | if self.contained_type is None: | |
1333 | return '%s [no contained value]' % self.typename | |
1334 | desc = "%s containing " % self.typename | |
1335 | if hasattr (self.visualizer, 'children'): | |
1336 | return desc + self.visualizer.to_string () | |
1337 | valtype = self._recognize (self.contained_type) | |
f9a27859 | 1338 | return desc + strip_versioned_namespace(str(valtype)) |
6f440cce JW |
1339 | |
1340 | class StdExpOptionalPrinter(SingleObjContainerPrinter): | |
00895372 | 1341 | "Print a std::optional or std::experimental::optional" |
6f440cce JW |
1342 | |
1343 | def __init__ (self, typename, val): | |
1344 | valtype = self._recognize (val.type.template_argument(0)) | |
d942bc80 JW |
1345 | typename = strip_versioned_namespace(typename) |
1346 | self.typename = re.sub('^std::(experimental::|)(fundamentals_v\d::|)(.*)', r'std::\1\3<%s>' % valtype, typename, 1) | |
1347 | payload = val['_M_payload'] | |
1348 | if self.typename.startswith('std::experimental'): | |
1349 | engaged = val['_M_engaged'] | |
1350 | contained_value = payload | |
1351 | else: | |
1352 | engaged = payload['_M_engaged'] | |
1353 | contained_value = payload['_M_payload'] | |
1354 | try: | |
1355 | # Since GCC 9 | |
1356 | contained_value = contained_value['_M_value'] | |
1357 | except: | |
1358 | pass | |
1359 | visualizer = gdb.default_visualizer (contained_value) | |
1360 | if not engaged: | |
1361 | contained_value = None | |
6f440cce JW |
1362 | super (StdExpOptionalPrinter, self).__init__ (contained_value, visualizer) |
1363 | ||
1364 | def to_string (self): | |
1365 | if self.contained_value is None: | |
7224c6a9 | 1366 | return "%s [no contained value]" % self.typename |
6f440cce | 1367 | if hasattr (self.visualizer, 'children'): |
7224c6a9 JW |
1368 | return "%s containing %s" % (self.typename, |
1369 | self.visualizer.to_string()) | |
6f440cce JW |
1370 | return self.typename |
1371 | ||
00895372 JW |
1372 | class StdVariantPrinter(SingleObjContainerPrinter): |
1373 | "Print a std::variant" | |
1374 | ||
1375 | def __init__(self, typename, val): | |
bab0a26d | 1376 | alternatives = get_template_arg_list(val.type) |
86397ed1 FD |
1377 | self.typename = strip_versioned_namespace(typename) |
1378 | self.typename = "%s<%s>" % (self.typename, ', '.join([self._recognize(alt) for alt in alternatives])) | |
00895372 JW |
1379 | self.index = val['_M_index'] |
1380 | if self.index >= len(alternatives): | |
1381 | self.contained_type = None | |
1382 | contained_value = None | |
1383 | visualizer = None | |
1384 | else: | |
1385 | self.contained_type = alternatives[int(self.index)] | |
449a4321 | 1386 | addr = val['_M_u']['_M_first']['_M_storage'].address |
00895372 JW |
1387 | contained_value = addr.cast(self.contained_type.pointer()).dereference() |
1388 | visualizer = gdb.default_visualizer(contained_value) | |
a0a1009a | 1389 | super (StdVariantPrinter, self).__init__(contained_value, visualizer, 'array') |
00895372 | 1390 | |
00895372 JW |
1391 | def to_string(self): |
1392 | if self.contained_value is None: | |
a0a1009a | 1393 | return "%s [no contained value]" % self.typename |
00895372 | 1394 | if hasattr(self.visualizer, 'children'): |
7224c6a9 JW |
1395 | return "%s [index %d] containing %s" % (self.typename, self.index, |
1396 | self.visualizer.to_string()) | |
a0a1009a | 1397 | return "%s [index %d]" % (self.typename, self.index) |
00895372 | 1398 | |
2dbe56bd JW |
1399 | class StdNodeHandlePrinter(SingleObjContainerPrinter): |
1400 | "Print a container node handle" | |
1401 | ||
1402 | def __init__(self, typename, val): | |
1403 | self.value_type = val.type.template_argument(1) | |
1404 | nodetype = val.type.template_argument(2).template_argument(0) | |
f9a27859 | 1405 | self.is_rb_tree_node = is_specialization_of(nodetype.name, '_Rb_tree_node') |
2dbe56bd JW |
1406 | self.is_map_node = val.type.template_argument(0) != self.value_type |
1407 | nodeptr = val['_M_ptr'] | |
1408 | if nodeptr: | |
1409 | if self.is_rb_tree_node: | |
1410 | contained_value = get_value_from_Rb_tree_node(nodeptr.dereference()) | |
1411 | else: | |
1412 | contained_value = get_value_from_aligned_membuf(nodeptr['_M_storage'], | |
1413 | self.value_type) | |
1414 | visualizer = gdb.default_visualizer(contained_value) | |
1415 | else: | |
1416 | contained_value = None | |
1417 | visualizer = None | |
1418 | optalloc = val['_M_alloc'] | |
1419 | self.alloc = optalloc['_M_payload'] if optalloc['_M_engaged'] else None | |
1420 | super(StdNodeHandlePrinter, self).__init__(contained_value, visualizer, | |
1421 | 'array') | |
1422 | ||
1423 | def to_string(self): | |
2dbe56bd JW |
1424 | desc = 'node handle for ' |
1425 | if not self.is_rb_tree_node: | |
1426 | desc += 'unordered ' | |
1427 | if self.is_map_node: | |
1428 | desc += 'map'; | |
1429 | else: | |
1430 | desc += 'set'; | |
1431 | ||
1432 | if self.contained_value: | |
1433 | desc += ' with element' | |
1434 | if hasattr(self.visualizer, 'children'): | |
1435 | return "%s = %s" % (desc, self.visualizer.to_string()) | |
1436 | return desc | |
1437 | else: | |
1438 | return 'empty %s' % desc | |
1439 | ||
6f440cce | 1440 | class StdExpStringViewPrinter: |
00895372 | 1441 | "Print a std::basic_string_view or std::experimental::basic_string_view" |
6f440cce JW |
1442 | |
1443 | def __init__ (self, typename, val): | |
1444 | self.val = val | |
1445 | ||
1446 | def to_string (self): | |
1447 | ptr = self.val['_M_str'] | |
1448 | len = self.val['_M_len'] | |
1449 | if hasattr (ptr, "lazy_string"): | |
1450 | return ptr.lazy_string (length = len) | |
1451 | return ptr.string (length = len) | |
1452 | ||
1453 | def display_hint (self): | |
1454 | return 'string' | |
8dfb08ab | 1455 | |
0ca7ba9a JW |
1456 | class StdExpPathPrinter: |
1457 | "Print a std::experimental::filesystem::path" | |
1458 | ||
1459 | def __init__ (self, typename, val): | |
1460 | self.val = val | |
a70384f9 | 1461 | self.typename = typename |
a00d74c4 JW |
1462 | start = self.val['_M_cmpts']['_M_impl']['_M_start'] |
1463 | finish = self.val['_M_cmpts']['_M_impl']['_M_finish'] | |
1464 | self.num_cmpts = int (finish - start) | |
1465 | ||
1466 | def _path_type(self): | |
1467 | t = str(self.val['_M_type']) | |
1468 | if t[-9:] == '_Root_dir': | |
1469 | return "root-directory" | |
1470 | if t[-10:] == '_Root_name': | |
1471 | return "root-name" | |
1472 | return None | |
0ca7ba9a JW |
1473 | |
1474 | def to_string (self): | |
a00d74c4 JW |
1475 | path = "%s" % self.val ['_M_pathname'] |
1476 | if self.num_cmpts == 0: | |
1477 | t = self._path_type() | |
1478 | if t: | |
1479 | path = '%s [%s]' % (path, t) | |
a70384f9 | 1480 | return "experimental::filesystem::path %s" % path |
a00d74c4 JW |
1481 | |
1482 | class _iterator(Iterator): | |
a70384f9 JW |
1483 | def __init__(self, cmpts, pathtype): |
1484 | self.pathtype = pathtype | |
a00d74c4 JW |
1485 | self.item = cmpts['_M_impl']['_M_start'] |
1486 | self.finish = cmpts['_M_impl']['_M_finish'] | |
1487 | self.count = 0 | |
1488 | ||
1489 | def __iter__(self): | |
1490 | return self | |
1491 | ||
1492 | def __next__(self): | |
1493 | if self.item == self.finish: | |
1494 | raise StopIteration | |
1495 | item = self.item.dereference() | |
1496 | count = self.count | |
1497 | self.count = self.count + 1 | |
1498 | self.item = self.item + 1 | |
1499 | path = item['_M_pathname'] | |
a70384f9 | 1500 | t = StdExpPathPrinter(self.pathtype, item)._path_type() |
a00d74c4 JW |
1501 | if not t: |
1502 | t = count | |
1503 | return ('[%s]' % t, path) | |
1504 | ||
1505 | def children(self): | |
a70384f9 | 1506 | return self._iterator(self.val['_M_cmpts'], self.typename) |
a00d74c4 | 1507 | |
4f87bb8d JW |
1508 | class StdPathPrinter: |
1509 | "Print a std::filesystem::path" | |
1510 | ||
1511 | def __init__ (self, typename, val): | |
1512 | self.val = val | |
1513 | self.typename = typename | |
a634928f | 1514 | impl = unique_ptr_get(self.val['_M_cmpts']['_M_impl']) |
4f87bb8d JW |
1515 | self.type = impl.cast(gdb.lookup_type('uintptr_t')) & 3 |
1516 | if self.type == 0: | |
1517 | self.impl = impl | |
1518 | else: | |
1519 | self.impl = None | |
1520 | ||
1521 | def _path_type(self): | |
1522 | t = str(self.type.cast(gdb.lookup_type(self.typename + '::_Type'))) | |
1523 | if t[-9:] == '_Root_dir': | |
1524 | return "root-directory" | |
1525 | if t[-10:] == '_Root_name': | |
1526 | return "root-name" | |
1527 | return None | |
1528 | ||
1529 | def to_string (self): | |
1530 | path = "%s" % self.val ['_M_pathname'] | |
1531 | if self.type != 0: | |
1532 | t = self._path_type() | |
1533 | if t: | |
1534 | path = '%s [%s]' % (path, t) | |
1535 | return "filesystem::path %s" % path | |
1536 | ||
1537 | class _iterator(Iterator): | |
1538 | def __init__(self, impl, pathtype): | |
a70384f9 | 1539 | self.pathtype = pathtype |
4f87bb8d JW |
1540 | if impl: |
1541 | # We can't access _Impl::_M_size because _Impl is incomplete | |
1542 | # so cast to int* to access the _M_size member at offset zero, | |
1543 | int_type = gdb.lookup_type('int') | |
1544 | cmpt_type = gdb.lookup_type(pathtype+'::_Cmpt') | |
1545 | char_type = gdb.lookup_type('char') | |
1546 | impl = impl.cast(int_type.pointer()) | |
1547 | size = impl.dereference() | |
1548 | #self.capacity = (impl + 1).dereference() | |
1549 | if hasattr(gdb.Type, 'alignof'): | |
1550 | sizeof_Impl = max(2 * int_type.sizeof, cmpt_type.alignof) | |
1551 | else: | |
1552 | sizeof_Impl = 2 * int_type.sizeof | |
1553 | begin = impl.cast(char_type.pointer()) + sizeof_Impl | |
1554 | self.item = begin.cast(cmpt_type.pointer()) | |
1555 | self.finish = self.item + size | |
1556 | self.count = 0 | |
1557 | else: | |
1558 | self.item = None | |
1559 | self.finish = None | |
1560 | ||
1561 | def __iter__(self): | |
1562 | return self | |
1563 | ||
1564 | def __next__(self): | |
1565 | if self.item == self.finish: | |
1566 | raise StopIteration | |
1567 | item = self.item.dereference() | |
1568 | count = self.count | |
1569 | self.count = self.count + 1 | |
1570 | self.item = self.item + 1 | |
1571 | path = item['_M_pathname'] | |
a70384f9 | 1572 | t = StdPathPrinter(self.pathtype, item)._path_type() |
4f87bb8d JW |
1573 | if not t: |
1574 | t = count | |
1575 | return ('[%s]' % t, path) | |
1576 | ||
1577 | def children(self): | |
1578 | return self._iterator(self.impl, self.typename) | |
1579 | ||
0ca7ba9a | 1580 | |
e1821582 JW |
1581 | class StdPairPrinter: |
1582 | "Print a std::pair object, with 'first' and 'second' as children" | |
1583 | ||
1584 | def __init__(self, typename, val): | |
1585 | self.val = val | |
1586 | ||
1587 | class _iter(Iterator): | |
1588 | "An iterator for std::pair types. Returns 'first' then 'second'." | |
1589 | ||
1590 | def __init__(self, val): | |
1591 | self.val = val | |
1592 | self.which = 'first' | |
1593 | ||
1594 | def __iter__(self): | |
1595 | return self | |
1596 | ||
1597 | def __next__(self): | |
1598 | if self.which is None: | |
1599 | raise StopIteration | |
1600 | which = self.which | |
1601 | if which == 'first': | |
1602 | self.which = 'second' | |
1603 | else: | |
1604 | self.which = None | |
1605 | return (which, self.val[which]) | |
1606 | ||
1607 | def children(self): | |
1608 | return self._iter(self.val) | |
1609 | ||
1610 | def to_string(self): | |
1611 | return None | |
1612 | ||
a59c50bd JW |
1613 | class StdCmpCatPrinter: |
1614 | "Print a comparison category object" | |
1615 | ||
1616 | def __init__ (self, typename, val): | |
1617 | self.typename = typename[typename.rfind(':')+1:] | |
1618 | self.val = val['_M_value'] | |
1619 | ||
1620 | def to_string (self): | |
1621 | if self.typename == 'strong_ordering' and self.val == 0: | |
1622 | name = 'equal' | |
1623 | else: | |
4540ef78 | 1624 | names = {2:'unordered', -1:'less', 0:'equivalent', 1:'greater'} |
a59c50bd JW |
1625 | name = names[int(self.val)] |
1626 | return 'std::{}::{}'.format(self.typename, name) | |
e1821582 | 1627 | |
2db38d9f JW |
1628 | class StdErrorCodePrinter: |
1629 | "Print a std::error_code or std::error_condition" | |
1630 | ||
36100e0e | 1631 | _system_is_posix = None # Whether std::system_category() use errno values. |
2db38d9f JW |
1632 | |
1633 | def __init__ (self, typename, val): | |
1634 | self.val = val | |
1918067e | 1635 | self.typename = strip_versioned_namespace(typename) |
2db38d9f | 1636 | # Do this only once ... |
36100e0e | 1637 | if StdErrorCodePrinter._system_is_posix is None: |
2db38d9f JW |
1638 | try: |
1639 | import posix | |
36100e0e | 1640 | StdErrorCodePrinter._system_is_posix = True |
2db38d9f | 1641 | except ImportError: |
36100e0e JW |
1642 | StdErrorCodePrinter._system_is_posix = False |
1643 | ||
1644 | @staticmethod | |
1645 | def _find_errc_enum(name): | |
1646 | typ = gdb.lookup_type(name) | |
1647 | if typ is not None and typ.code == gdb.TYPE_CODE_ENUM: | |
1648 | return typ | |
1649 | return None | |
1650 | ||
11e1ee1b JW |
1651 | @classmethod |
1652 | def _find_standard_errc_enum(cls, name): | |
1653 | for ns in ['', _versioned_namespace]: | |
1654 | try: | |
1655 | qname = 'std::{}{}'.format(ns, name) | |
1656 | return cls._find_errc_enum(qname) | |
1657 | except RuntimeError: | |
1658 | pass | |
1659 | ||
36100e0e JW |
1660 | @classmethod |
1661 | def _match_net_ts_category(cls, cat): | |
1662 | net_cats = ['stream', 'socket', 'ip::resolver'] | |
1663 | for c in net_cats: | |
1664 | func = c + '_category()' | |
1665 | for ns in ['', _versioned_namespace]: | |
1666 | ns = 'std::{}experimental::net::v1'.format(ns) | |
1667 | sym = gdb.lookup_symbol('{}::{}::__c'.format(ns, func))[0] | |
1668 | if sym is not None: | |
1669 | if cat == sym.value().address: | |
1670 | name = 'net::' + func | |
1671 | enum = cls._find_errc_enum('{}::{}_errc'.format(ns, c)) | |
1672 | return (name, enum) | |
1673 | return (None, None) | |
1674 | ||
1675 | @classmethod | |
1676 | def _category_info(cls, cat): | |
1677 | "Return details of a std::error_category" | |
1678 | ||
1679 | name = None | |
1680 | enum = None | |
1681 | is_errno = False | |
1682 | ||
1683 | # Try these first, or we get "warning: RTTI symbol not found" when | |
1684 | # using cat.dynamic_type on the local class types for Net TS categories. | |
1685 | func, enum = cls._match_net_ts_category(cat) | |
1686 | if func is not None: | |
1687 | return (None, func, enum, is_errno) | |
1688 | ||
1689 | # This might give a warning for a program-defined category defined as | |
1690 | # a local class, but there doesn't seem to be any way to avoid that. | |
1691 | typ = cat.dynamic_type.target() | |
1692 | # Shortcuts for the known categories defined by libstdc++. | |
1693 | if typ.tag.endswith('::generic_error_category'): | |
1694 | name = 'generic' | |
1695 | is_errno = True | |
1696 | if typ.tag.endswith('::system_error_category'): | |
1697 | name = 'system' | |
1698 | is_errno = cls._system_is_posix | |
1699 | if typ.tag.endswith('::future_error_category'): | |
1700 | name = 'future' | |
11e1ee1b | 1701 | enum = cls._find_standard_errc_enum('future_errc') |
36100e0e JW |
1702 | if typ.tag.endswith('::io_error_category'): |
1703 | name = 'io' | |
11e1ee1b | 1704 | enum = cls._find_standard_errc_enum('io_errc') |
36100e0e JW |
1705 | |
1706 | if name is None: | |
1707 | try: | |
1708 | # Want to call std::error_category::name() override, but it's | |
1709 | # unsafe: https://sourceware.org/bugzilla/show_bug.cgi?id=28856 | |
1710 | # gdb.set_convenience_variable('__cat', cat) | |
1711 | # return '"%s"' % gdb.parse_and_eval('$__cat->name()').string() | |
1712 | pass | |
1713 | except: | |
2db38d9f | 1714 | pass |
36100e0e | 1715 | return (name, typ.tag, enum, is_errno) |
2db38d9f JW |
1716 | |
1717 | @staticmethod | |
36100e0e JW |
1718 | def _unqualified_name(name): |
1719 | "Strip any nested-name-specifier from NAME to give an unqualified name" | |
1720 | return name.split('::')[-1] | |
2db38d9f JW |
1721 | |
1722 | def to_string (self): | |
1723 | value = self.val['_M_value'] | |
36100e0e JW |
1724 | cat = self.val['_M_cat'] |
1725 | name, alt_name, enum, is_errno = self._category_info(cat) | |
2db38d9f | 1726 | if value == 0: |
36100e0e JW |
1727 | default_cats = { 'error_code' : 'system', |
1728 | 'error_condition' : 'generic' } | |
1729 | if name == default_cats[self._unqualified_name(self.typename)]: | |
2db38d9f | 1730 | return self.typename + ' = { }' # default-constructed value |
36100e0e JW |
1731 | |
1732 | strval = str(value) | |
1733 | if is_errno and value != 0: | |
2db38d9f JW |
1734 | try: |
1735 | strval = errno.errorcode[int(value)] | |
1736 | except: | |
1737 | pass | |
36100e0e JW |
1738 | elif enum is not None: |
1739 | strval = self._unqualified_name(str(value.cast(enum))) | |
1740 | ||
1741 | if name is not None: | |
1742 | name = '"%s"' % name | |
1743 | else: | |
1744 | name = alt_name | |
1745 | return '%s = {%s: %s}' % (self.typename, name, strval) | |
1746 | ||
2db38d9f | 1747 | |
7a2f2d91 JW |
1748 | class StdRegexStatePrinter: |
1749 | "Print a state node in the NFA for a std::regex" | |
1750 | ||
1751 | def __init__ (self, typename, val): | |
1752 | self.val = val | |
1753 | self.typename = typename | |
1754 | ||
1755 | def to_string (self): | |
1756 | opcode = str(self.val['_M_opcode']) | |
1757 | if opcode: | |
1758 | opcode = opcode[25:] | |
1759 | next_id = self.val['_M_next'] | |
1760 | ||
1761 | variants = {'repeat':'alt', 'alternative':'alt', | |
1762 | 'subexpr_begin':'subexpr', 'subexpr_end':'subexpr', | |
1763 | 'line_begin_assertion':None, 'line_end_assertion':None, | |
1764 | 'word_boundary':'neg', 'subexpr_lookahead':'neg', | |
1765 | 'backref':'backref_index', | |
1766 | 'match':None, 'accept':None, | |
1767 | 'dummy':None, 'unknown':None | |
1768 | } | |
1769 | v = variants[opcode] | |
1770 | ||
1771 | s = "opcode={}, next={}".format(opcode, next_id) | |
1772 | if v is not None and self.val['_M_' + v] is not None: | |
1773 | s = "{}, {}={}".format(s, v, self.val['_M_' + v]) | |
1774 | return "{%s}" % (s) | |
1775 | ||
fdb3f82f PF |
1776 | class StdSpanPrinter: |
1777 | "Print a std::span" | |
1778 | ||
71999fde | 1779 | class iterator(Iterator): |
fdb3f82f PF |
1780 | def __init__(self, begin, size): |
1781 | self.count = 0 | |
1782 | self.begin = begin | |
1783 | self.size = size | |
1784 | ||
1785 | def __iter__ (self): | |
1786 | return self | |
1787 | ||
1788 | def __next__ (self): | |
1789 | if self.count == self.size: | |
1790 | raise StopIteration | |
1791 | ||
1792 | count = self.count | |
1793 | self.count = self.count + 1 | |
1794 | return '[%d]' % count, (self.begin + count).dereference() | |
1795 | ||
1796 | def __init__(self, typename, val): | |
ace4b7f2 | 1797 | self.typename = strip_versioned_namespace(typename) |
fdb3f82f PF |
1798 | self.val = val |
1799 | if val.type.template_argument(1) == gdb.parse_and_eval('static_cast<std::size_t>(-1)'): | |
1800 | self.size = val['_M_extent']['_M_extent_value'] | |
1801 | else: | |
1802 | self.size = val.type.template_argument(1) | |
1803 | ||
1804 | def to_string(self): | |
1805 | return '%s of length %d' % (self.typename, self.size) | |
1806 | ||
1807 | def children(self): | |
71999fde PF |
1808 | return self.iterator(self.val['_M_ptr'], self.size) |
1809 | ||
1810 | def display_hint(self): | |
1811 | return 'array' | |
1812 | ||
1813 | class StdInitializerListPrinter: | |
1814 | "Print a std::initializer_list" | |
1815 | ||
1816 | def __init__(self, typename, val): | |
1817 | self.typename = typename | |
1818 | self.val = val | |
1819 | self.size = val['_M_len'] | |
1820 | ||
1821 | def to_string(self): | |
1822 | return '%s of length %d' % (self.typename, self.size) | |
1823 | ||
1824 | def children(self): | |
1825 | return StdSpanPrinter.iterator(self.val['_M_array'], self.size) | |
fdb3f82f PF |
1826 | |
1827 | def display_hint(self): | |
1828 | return 'array' | |
1829 | ||
a8495845 JW |
1830 | class StdAtomicPrinter: |
1831 | "Print a std:atomic" | |
1832 | ||
1833 | def __init__(self, typename, val): | |
11e1ee1b | 1834 | self.typename = strip_versioned_namespace(typename) |
a8495845 JW |
1835 | self.val = val |
1836 | self.shptr_printer = None | |
1837 | self.value_type = self.val.type.template_argument(0) | |
1838 | if self.value_type.tag is not None: | |
1839 | typ = strip_versioned_namespace(self.value_type.tag) | |
1840 | if typ.startswith('std::shared_ptr<') or typ.startswith('std::weak_ptr<'): | |
1841 | impl = val['_M_impl'] | |
1842 | self.shptr_printer = SharedPointerPrinter(typename, impl) | |
1843 | self.children = self._shptr_children | |
1844 | ||
1845 | def _shptr_children(self): | |
1846 | return SmartPtrIterator(self.shptr_printer.pointer) | |
1847 | ||
1848 | def to_string(self): | |
1849 | if self.shptr_printer is not None: | |
1850 | return self.shptr_printer.to_string() | |
1851 | ||
1852 | if self.value_type.code == gdb.TYPE_CODE_INT: | |
1853 | val = self.val['_M_i'] | |
1854 | elif self.value_type.code == gdb.TYPE_CODE_FLT: | |
1855 | val = self.val['_M_fp'] | |
1856 | elif self.value_type.code == gdb.TYPE_CODE_PTR: | |
1857 | val = self.val['_M_b']['_M_p'] | |
1858 | elif self.value_type.code == gdb.TYPE_CODE_BOOL: | |
1859 | val = self.val['_M_base']['_M_i'] | |
1860 | else: | |
1861 | val = self.val['_M_i'] | |
1862 | return '%s<%s> = { %s }' % (self.typename, str(self.value_type), val) | |
1863 | ||
1d9454ab JW |
1864 | class StdFormatArgsPrinter: |
1865 | "Print a std::basic_format_args" | |
1866 | # TODO: add printer for basic_format_arg<C> and print out children | |
1867 | # TODO: add printer for basic_format_args<C>::_Store<Args...> | |
1868 | ||
1869 | def __init__(self, typename, val): | |
1870 | self.typename = strip_versioned_namespace(typename) | |
1871 | self.val = val | |
1872 | ||
1873 | def to_string(self): | |
1874 | targs = get_template_arg_list(self.val.type) | |
1875 | char_type = get_template_arg_list(targs[0])[1] | |
1876 | if char_type == gdb.lookup_type('char'): | |
1877 | typ = 'std::format_args' | |
1878 | elif char_type == gdb.lookup_type('wchar_t'): | |
1879 | typ = 'std::wformat_args' | |
1880 | else: | |
1881 | typ = 'std::basic_format_args' | |
1882 | ||
1883 | size = self.val['_M_packed_size'] | |
1884 | if size == 1: | |
1885 | return "%s with 1 argument" % (typ) | |
1886 | if size == 0: | |
1887 | size = self.val['_M_unpacked_size'] | |
1888 | return "%s with %d arguments" % (typ, size) | |
1889 | ||
d33a250f JW |
1890 | def std_ratio_t_tuple(ratio_type): |
1891 | # TODO use reduced period i.e. duration::period | |
1892 | period = self.val.type.template_argument(1) | |
1893 | num = period.template_argument(0) | |
1894 | den = period.template_argument(1) | |
1895 | return (num, den) | |
1896 | ||
1897 | class StdChronoDurationPrinter: | |
1898 | "Print a std::chrono::duration" | |
1899 | ||
1900 | def __init__(self, typename, val): | |
1901 | self.typename = strip_versioned_namespace(typename) | |
1902 | self.val = val | |
1903 | ||
1904 | def _ratio(self): | |
1905 | # TODO use reduced period i.e. duration::period | |
1906 | period = self.val.type.template_argument(1) | |
1907 | num = period.template_argument(0) | |
1908 | den = period.template_argument(1) | |
1909 | return (num, den) | |
1910 | ||
1911 | def _suffix(self): | |
1912 | num, den = self._ratio() | |
1913 | if num == 1: | |
1914 | if den == 1: | |
1915 | return 's' | |
1916 | if den == 1000: | |
1917 | return 'ms' | |
1918 | if den == 1000000: | |
1919 | return 'us' | |
1920 | if den == 1000000000: | |
1921 | return 'ns' | |
1922 | elif den == 1: | |
1923 | if num == 60: | |
1924 | return 'min' | |
1925 | if num == 3600: | |
1926 | return 'h' | |
1927 | if num == 86400: | |
1928 | return 'd' | |
1929 | return '[{}]s'.format(num) | |
1930 | return "[{}/{}]s".format(num, den) | |
1931 | ||
1932 | def to_string(self): | |
1933 | return "std::chrono::duration = { %d%s }" % (self.val['__r'], self._suffix()) | |
1934 | ||
1935 | ||
1936 | class StdChronoTimePointPrinter: | |
1937 | "Print a std::chrono::time_point" | |
1938 | ||
1939 | def __init__(self, typename, val): | |
1940 | self.typename = strip_versioned_namespace(typename) | |
1941 | self.val = val | |
1942 | ||
1943 | def _clock(self): | |
1944 | clock = self.val.type.template_argument(0) | |
1945 | name = strip_versioned_namespace(clock.name) | |
1946 | if name == 'std::chrono::_V2::system_clock' \ | |
1947 | or name == 'std::chrono::system_clock': | |
1948 | return ('std::chrono::sys_time', 0) | |
1949 | # XXX need to remove leap seconds from utc, gps, and tai | |
1950 | #if name == 'std::chrono::utc_clock': | |
1951 | # return ('std::chrono::utc_time', 0) | |
1952 | #if name == 'std::chrono::gps_clock': | |
1953 | # return ('std::chrono::gps_clock time_point', 315964809) | |
1954 | #if name == 'std::chrono::tai_clock': | |
1955 | # return ('std::chrono::tai_clock time_point', -378691210) | |
1956 | if name == 'std::filesystem::__file_clock': | |
1957 | return ('std::chrono::file_time', 6437664000) | |
1958 | if name == 'std::chrono::local_t': | |
1959 | return ('std::chrono::local_time', 0) | |
1960 | return ('{} time_point'.format(name), None) | |
1961 | ||
1962 | def to_string(self): | |
1963 | clock, offset = self._clock() | |
1964 | d = self.val['__d'] | |
1965 | r = d['__r'] | |
1966 | printer = StdChronoDurationPrinter(d.type.name, d) | |
1967 | suffix = printer._suffix() | |
1968 | time = '' | |
1969 | if offset is not None: | |
1970 | num, den = printer._ratio() | |
1971 | secs = (r * num / den) + offset | |
1972 | try: | |
80ff207d | 1973 | dt = datetime.fromtimestamp(secs, _utc_timezone) |
d33a250f JW |
1974 | time = ' [{:%Y-%m-%d %H:%M:%S}]'.format(dt) |
1975 | except: | |
1976 | pass | |
1977 | return '%s = {%d%s%s}' % (clock, r, suffix, time) | |
1978 | ||
1979 | class StdChronoZonedTimePrinter: | |
1980 | "Print a std::chrono::zoned_time" | |
1981 | ||
1982 | def __init__(self, typename, val): | |
1983 | self.typename = strip_versioned_namespace(typename) | |
1984 | self.val = val | |
1985 | ||
1986 | def to_string(self): | |
1987 | zone = self.val['_M_zone'].dereference() | |
1988 | time = self.val['_M_tp'] | |
1989 | return 'std::chrono::zoned_time = {{{} {}}}'.format(zone, time) | |
1990 | ||
1991 | ||
1992 | months = [None, 'January', 'February', 'March', 'April', 'May', 'June', | |
1993 | 'July', 'August', 'September', 'October', 'November', 'December'] | |
1994 | ||
1995 | weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', | |
1996 | 'Saturday', 'Sunday'] | |
1997 | ||
1998 | class StdChronoCalendarPrinter: | |
1999 | "Print a std::chrono::day, std::chrono::month, std::chrono::year etc." | |
2000 | ||
2001 | def __init__(self, typename, val): | |
2002 | self.typename = strip_versioned_namespace(typename) | |
2003 | self.val = val | |
2004 | ||
2005 | def to_string(self): | |
2006 | val = self.val | |
2007 | typ = self.typename | |
2008 | if 'month' in typ and typ != 'std::chrono::year_month_day_last': | |
2009 | m = val['_M_m'] | |
2010 | if typ.startswith('std::chrono::year'): | |
2011 | y = val['_M_y'] | |
2012 | ||
2013 | if typ == 'std::chrono::day': | |
2014 | return '{}'.format(int(val['_M_d'])) | |
2015 | if typ == 'std::chrono::month': | |
2016 | return months[m] | |
2017 | if typ == 'std::chrono::year': | |
2018 | return '{}y'.format(y) | |
2019 | if typ == 'std::chrono::weekday': | |
2020 | return '{}'.format(weekdays[val['_M_wd']]) | |
2021 | if typ == 'std::chrono::weekday_indexed': | |
2022 | return '{}[{}]'.format(val['_M_wd'], int(val['_M_index'])) | |
2023 | if typ == 'std::chrono::weekday_last': | |
2024 | return '{}[last]'.format(val['_M_wd']) | |
2025 | if typ == 'std::chrono::month_day': | |
2026 | return '{}/{}'.format(m, val['_M_d']) | |
2027 | if typ == 'std::chrono::month_day_last': | |
2028 | return '{}/last'.format(m) | |
2029 | if typ == 'std::chrono::month_weekday': | |
2030 | return '{}/{}'.format(m, val['_M_wdi']) | |
2031 | if typ == 'std::chrono::month_weekday_last': | |
2032 | return '{}/{}'.format(m, val['_M_wdl']) | |
2033 | if typ == 'std::chrono::year_month': | |
2034 | return '{}/{}'.format(y, m) | |
2035 | if typ == 'std::chrono::year_month_day': | |
2036 | return '{}/{}/{}'.format(y, m, val['_M_d']) | |
2037 | if typ == 'std::chrono::year_month_day_last': | |
2038 | return '{}/{}'.format(y, val['_M_mdl']) | |
2039 | if typ == 'std::chrono::year_month_weekday': | |
2040 | return '{}/{}'.format(y, m, val['_M_wdi']) | |
2041 | if typ == 'std::chrono::year_month_weekday_last': | |
2042 | return '{}/{}'.format(y, m, val['_M_wdl']) | |
2043 | if typ.startswith('std::chrono::hh_mm_ss'): | |
2044 | fract = '' | |
2045 | if val['fractional_width'] != 0: | |
2046 | fract = '.{:0{}d}'.format(int(val['_M_ss']['__r']), | |
2047 | int(val['fractional_width'])) | |
2048 | h = int(val['_M_h']['__r']) | |
2049 | m = int(val['_M_m']['__r']) | |
2050 | s = int(val['_M_s']['__r']) | |
2051 | if val['_M_is_neg']: | |
2052 | h = -h | |
2053 | return '{:02}:{:02}:{:02}{}'.format(h, m, s, fract) | |
2054 | ||
2055 | class StdChronoTimeZonePrinter: | |
2056 | "Print a chrono::time_zone or chrono::time_zone_link" | |
2057 | ||
2058 | def __init__(self, typename, val): | |
2059 | self.typename = strip_versioned_namespace(typename) | |
2060 | self.val = val | |
2061 | ||
2062 | def to_string(self): | |
2063 | str = '%s %s' % (self.typename, self.val['_M_name']) | |
2064 | if self.typename.endswith("_link"): | |
2065 | str += ' -> %s' % (self.val['_M_target']) | |
2066 | return str | |
2067 | ||
2068 | class StdChronoLeapSecondPrinter: | |
2069 | "Print a chrono::leap_second" | |
2070 | ||
2071 | def __init__(self, typename, val): | |
2072 | self.typename = strip_versioned_namespace(typename) | |
2073 | self.val = val | |
2074 | ||
2075 | def to_string(self): | |
2076 | date = self.val['_M_s']['__r'] | |
2077 | neg = '+-'[date < 0] | |
2078 | return '%s %d (%c)' % (self.typename, abs(date), neg) | |
2079 | ||
2080 | class StdChronoTzdbPrinter: | |
2081 | "Print a chrono::tzdb" | |
2082 | ||
2083 | def __init__(self, typename, val): | |
2084 | self.typename = strip_versioned_namespace(typename) | |
2085 | self.val = val | |
2086 | ||
2087 | def to_string(self): | |
2088 | return '%s %s' % (self.typename, self.val['version']) | |
2089 | ||
2090 | class StdChronoTimeZoneRulePrinter: | |
2091 | "Print a chrono::time_zone rule" | |
2092 | ||
2093 | def __init__(self, typename, val): | |
2094 | self.typename = strip_versioned_namespace(typename) | |
2095 | self.val = val | |
2096 | ||
2097 | def to_string(self): | |
2098 | on = self.val['on'] | |
2099 | kind = on['kind'] | |
2100 | month = months[on['month']] | |
2101 | suffixes = {1:'st', 2:'nd', 3:'rd', 21:'st', 22:'nd', 23:'rd', 31:'st'} | |
2102 | day = on['day_of_month'] | |
2103 | ordinal_day = '{}{}'.format(day, suffixes.get(day, 'th')) | |
2104 | if kind == 0: # DayOfMonth | |
2105 | start = '{} {}{}'.format(month, ordinal_day) | |
2106 | else: | |
2107 | weekday = weekdays[on['day_of_week']] | |
2108 | if kind == 1: # LastWeekDay | |
2109 | start = 'last {} in {}'.format(weekday, month) | |
2110 | else: | |
2111 | if kind == 2: # LessEq | |
2112 | direction = ('last', '<=') | |
2113 | else: | |
2114 | direction = ('first', '>=') | |
2115 | day = on['day_of_month'] | |
2116 | start = '{} {} {} {} {}'.format(direction[0], weekday, | |
2117 | direction[1], month, | |
2118 | ordinal_day) | |
2119 | return 'time_zone rule {} from {} to {} starting on {}'.format( | |
2120 | self.val['name'], self.val['from'], self.val['to'], start) | |
2121 | ||
2122 | ||
d63c53cc TT |
2123 | # A "regular expression" printer which conforms to the |
2124 | # "SubPrettyPrinter" protocol from gdb.printing. | |
2125 | class RxPrinter(object): | |
2126 | def __init__(self, name, function): | |
2127 | super(RxPrinter, self).__init__() | |
2128 | self.name = name | |
2129 | self.function = function | |
2130 | self.enabled = True | |
2131 | ||
2132 | def invoke(self, value): | |
2133 | if not self.enabled: | |
2134 | return None | |
c77efe8f PM |
2135 | |
2136 | if value.type.code == gdb.TYPE_CODE_REF: | |
2137 | if hasattr(gdb.Value,"referenced_value"): | |
2138 | value = value.referenced_value() | |
2139 | ||
d63c53cc TT |
2140 | return self.function(self.name, value) |
2141 | ||
2142 | # A pretty-printer that conforms to the "PrettyPrinter" protocol from | |
2143 | # gdb.printing. It can also be used directly as an old-style printer. | |
2144 | class Printer(object): | |
2145 | def __init__(self, name): | |
2146 | super(Printer, self).__init__() | |
2147 | self.name = name | |
2148 | self.subprinters = [] | |
2149 | self.lookup = {} | |
2150 | self.enabled = True | |
6f440cce | 2151 | self.compiled_rx = re.compile('^([a-zA-Z0-9_:]+)(<.*>)?$') |
d63c53cc TT |
2152 | |
2153 | def add(self, name, function): | |
2154 | # A small sanity check. | |
2155 | # FIXME | |
6f440cce | 2156 | if not self.compiled_rx.match(name): |
47059228 | 2157 | raise ValueError('libstdc++ programming error: "%s" does not match' % name) |
d63c53cc TT |
2158 | printer = RxPrinter(name, function) |
2159 | self.subprinters.append(printer) | |
2160 | self.lookup[name] = printer | |
41850419 | 2161 | |
3efe2bf7 TT |
2162 | # Add a name using _GLIBCXX_BEGIN_NAMESPACE_VERSION. |
2163 | def add_version(self, base, name, function): | |
2164 | self.add(base + name, function) | |
4347fea9 | 2165 | if _versioned_namespace and not '__cxx11' in base: |
86397ed1 FD |
2166 | vbase = re.sub('^(std|__gnu_cxx)::', r'\g<0>%s' % _versioned_namespace, base) |
2167 | self.add(vbase + name, function) | |
3efe2bf7 TT |
2168 | |
2169 | # Add a name using _GLIBCXX_BEGIN_NAMESPACE_CONTAINER. | |
2170 | def add_container(self, base, name, function): | |
2171 | self.add_version(base, name, function) | |
2172 | self.add_version(base + '__cxx1998::', name, function) | |
2173 | ||
d63c53cc TT |
2174 | @staticmethod |
2175 | def get_basic_type(type): | |
2176 | # If it points to a reference, get the reference. | |
2177 | if type.code == gdb.TYPE_CODE_REF: | |
2178 | type = type.target () | |
41850419 | 2179 | |
d63c53cc TT |
2180 | # Get the unqualified type, stripped of typedefs. |
2181 | type = type.unqualified ().strip_typedefs () | |
41850419 | 2182 | |
d63c53cc | 2183 | return type.tag |
41850419 | 2184 | |
d63c53cc TT |
2185 | def __call__(self, val): |
2186 | typename = self.get_basic_type(val.type) | |
2187 | if not typename: | |
2188 | return None | |
41850419 | 2189 | |
d63c53cc TT |
2190 | # All the types we match are template types, so we can use a |
2191 | # dictionary. | |
2192 | match = self.compiled_rx.match(typename) | |
2193 | if not match: | |
2194 | return None | |
41850419 | 2195 | |
d63c53cc | 2196 | basename = match.group(1) |
c77efe8f PM |
2197 | |
2198 | if val.type.code == gdb.TYPE_CODE_REF: | |
2199 | if hasattr(gdb.Value,"referenced_value"): | |
2200 | val = val.referenced_value() | |
2201 | ||
d63c53cc TT |
2202 | if basename in self.lookup: |
2203 | return self.lookup[basename].invoke(val) | |
41850419 | 2204 | |
d63c53cc | 2205 | # Cannot find a pretty printer. Return None. |
41850419 TT |
2206 | return None |
2207 | ||
d63c53cc TT |
2208 | libstdcxx_printer = None |
2209 | ||
077aa95e | 2210 | class TemplateTypePrinter(object): |
7224c6a9 | 2211 | r""" |
bab0a26d | 2212 | A type printer for class templates with default template arguments. |
077aa95e | 2213 | |
bab0a26d JW |
2214 | Recognizes specializations of class templates and prints them without |
2215 | any template arguments that use a default template argument. | |
2216 | Type printers are recursively applied to the template arguments. | |
077aa95e | 2217 | |
bab0a26d | 2218 | e.g. replace "std::vector<T, std::allocator<T> >" with "std::vector<T>". |
077aa95e JW |
2219 | """ |
2220 | ||
bab0a26d | 2221 | def __init__(self, name, defargs): |
077aa95e | 2222 | self.name = name |
bab0a26d | 2223 | self.defargs = defargs |
077aa95e JW |
2224 | self.enabled = True |
2225 | ||
2226 | class _recognizer(object): | |
bab0a26d JW |
2227 | "The recognizer class for TemplateTypePrinter." |
2228 | ||
2229 | def __init__(self, name, defargs): | |
2230 | self.name = name | |
2231 | self.defargs = defargs | |
2232 | # self.type_obj = None | |
077aa95e JW |
2233 | |
2234 | def recognize(self, type_obj): | |
bab0a26d JW |
2235 | """ |
2236 | If type_obj is a specialization of self.name that uses all the | |
2237 | default template arguments for the class template, then return | |
2238 | a string representation of the type without default arguments. | |
2239 | Otherwise, return None. | |
2240 | """ | |
2241 | ||
077aa95e JW |
2242 | if type_obj.tag is None: |
2243 | return None | |
2244 | ||
bab0a26d JW |
2245 | if not type_obj.tag.startswith(self.name): |
2246 | return None | |
2247 | ||
2248 | template_args = get_template_arg_list(type_obj) | |
2249 | displayed_args = [] | |
2250 | require_defaulted = False | |
2251 | for n in range(len(template_args)): | |
2252 | # The actual template argument in the type: | |
2253 | targ = template_args[n] | |
2254 | # The default template argument for the class template: | |
2255 | defarg = self.defargs.get(n) | |
2256 | if defarg is not None: | |
2257 | # Substitute other template arguments into the default: | |
2258 | defarg = defarg.format(*template_args) | |
2259 | # Fail to recognize the type (by returning None) | |
2260 | # unless the actual argument is the same as the default. | |
2261 | try: | |
2262 | if targ != gdb.lookup_type(defarg): | |
2263 | return None | |
2264 | except gdb.error: | |
2265 | # Type lookup failed, just use string comparison: | |
2266 | if targ.tag != defarg: | |
2267 | return None | |
2268 | # All subsequent args must have defaults: | |
2269 | require_defaulted = True | |
2270 | elif require_defaulted: | |
2271 | return None | |
2272 | else: | |
2273 | # Recursively apply recognizers to the template argument | |
2274 | # and add it to the arguments that will be displayed: | |
2275 | displayed_args.append(self._recognize_subtype(targ)) | |
2276 | ||
2277 | # This assumes no class templates in the nested-name-specifier: | |
2278 | template_name = type_obj.tag[0:type_obj.tag.find('<')] | |
2279 | template_name = strip_inline_namespaces(template_name) | |
2280 | ||
2281 | return template_name + '<' + ', '.join(displayed_args) + '>' | |
2282 | ||
2283 | def _recognize_subtype(self, type_obj): | |
2284 | """Convert a gdb.Type to a string by applying recognizers, | |
2285 | or if that fails then simply converting to a string.""" | |
2286 | ||
2287 | if type_obj.code == gdb.TYPE_CODE_PTR: | |
2288 | return self._recognize_subtype(type_obj.target()) + '*' | |
2289 | if type_obj.code == gdb.TYPE_CODE_ARRAY: | |
2290 | type_str = self._recognize_subtype(type_obj.target()) | |
2291 | if str(type_obj.strip_typedefs()).endswith('[]'): | |
2292 | return type_str + '[]' # array of unknown bound | |
2293 | return "%s[%d]" % (type_str, type_obj.range()[1] + 1) | |
2294 | if type_obj.code == gdb.TYPE_CODE_REF: | |
2295 | return self._recognize_subtype(type_obj.target()) + '&' | |
2296 | if hasattr(gdb, 'TYPE_CODE_RVALUE_REF'): | |
2297 | if type_obj.code == gdb.TYPE_CODE_RVALUE_REF: | |
2298 | return self._recognize_subtype(type_obj.target()) + '&&' | |
2299 | ||
2300 | type_str = gdb.types.apply_type_recognizers( | |
2301 | gdb.types.get_type_recognizers(), type_obj) | |
2302 | if type_str: | |
2303 | return type_str | |
2304 | return str(type_obj) | |
077aa95e JW |
2305 | |
2306 | def instantiate(self): | |
bab0a26d JW |
2307 | "Return a recognizer object for this type printer." |
2308 | return self._recognizer(self.name, self.defargs) | |
077aa95e | 2309 | |
bab0a26d JW |
2310 | def add_one_template_type_printer(obj, name, defargs): |
2311 | r""" | |
2312 | Add a type printer for a class template with default template arguments. | |
2313 | ||
2314 | Args: | |
2315 | name (str): The template-name of the class template. | |
2316 | defargs (dict int:string) The default template arguments. | |
2317 | ||
2318 | Types in defargs can refer to the Nth template-argument using {N} | |
2319 | (with zero-based indices). | |
2320 | ||
2321 | e.g. 'unordered_map' has these defargs: | |
2322 | { 2: 'std::hash<{0}>', | |
2323 | 3: 'std::equal_to<{0}>', | |
2324 | 4: 'std::allocator<std::pair<const {0}, {1}> >' } | |
2325 | ||
2326 | """ | |
2327 | printer = TemplateTypePrinter('std::'+name, defargs) | |
077aa95e | 2328 | gdb.types.register_type_printer(obj, printer) |
3997383b FD |
2329 | |
2330 | # Add type printer for same type in debug namespace: | |
2331 | printer = TemplateTypePrinter('std::__debug::'+name, defargs) | |
2332 | gdb.types.register_type_printer(obj, printer) | |
2333 | ||
4347fea9 | 2334 | if _versioned_namespace and not '__cxx11' in name: |
f9a27859 | 2335 | # Add second type printer for same type in versioned namespace: |
bab0a26d | 2336 | ns = 'std::' + _versioned_namespace |
4fdb6fb6 JW |
2337 | # PR 86112 Cannot use dict comprehension here: |
2338 | defargs = dict((n, d.replace('std::', ns)) for (n,d) in defargs.items()) | |
bab0a26d | 2339 | printer = TemplateTypePrinter(ns+name, defargs) |
f9a27859 | 2340 | gdb.types.register_type_printer(obj, printer) |
077aa95e | 2341 | |
13337ea9 FD |
2342 | # Add type printer for same type in debug namespace: |
2343 | printer = TemplateTypePrinter('std::__debug::'+name, defargs) | |
2344 | gdb.types.register_type_printer(obj, printer) | |
2345 | ||
50605a7f | 2346 | class FilteringTypePrinter(object): |
bab0a26d JW |
2347 | r""" |
2348 | A type printer that uses typedef names for common template specializations. | |
2349 | ||
2350 | Args: | |
2b7f0378 | 2351 | template (str): The class template to recognize. |
bab0a26d | 2352 | name (str): The typedef-name that will be used instead. |
92281622 | 2353 | targ1 (str, optional): The first template argument. Defaults to None. |
bab0a26d | 2354 | |
2b7f0378 | 2355 | Checks if a specialization of the class template 'template' is the same type |
bab0a26d JW |
2356 | as the typedef 'name', and prints it as 'name' instead. |
2357 | ||
2358 | e.g. if an instantiation of std::basic_istream<C, T> is the same type as | |
2359 | std::istream then print it as std::istream. | |
92281622 JW |
2360 | |
2361 | If targ1 is provided (not None), match only template specializations with | |
2362 | this type as the first template argument, e.g. if template='basic_string' | |
2363 | and targ1='char' then only match 'basic_string<char,...>' and not | |
2364 | 'basic_string<wchar_t,...>'. This rejects non-matching specializations | |
2365 | more quickly, without needing to do GDB type lookups. | |
bab0a26d JW |
2366 | """ |
2367 | ||
92281622 | 2368 | def __init__(self, template, name, targ1 = None): |
2b7f0378 | 2369 | self.template = template |
50605a7f | 2370 | self.name = name |
2b7f0378 | 2371 | self.targ1 = targ1 |
50605a7f TT |
2372 | self.enabled = True |
2373 | ||
2374 | class _recognizer(object): | |
ace4b7f2 | 2375 | "The recognizer class for FilteringTypePrinter." |
bab0a26d | 2376 | |
2b7f0378 FD |
2377 | def __init__(self, template, name, targ1): |
2378 | self.template = template | |
50605a7f | 2379 | self.name = name |
2b7f0378 | 2380 | self.targ1 = targ1 |
50605a7f TT |
2381 | self.type_obj = None |
2382 | ||
2383 | def recognize(self, type_obj): | |
bab0a26d | 2384 | """ |
2b7f0378 | 2385 | If type_obj starts with self.template and is the same type as |
bab0a26d JW |
2386 | self.name then return self.name, otherwise None. |
2387 | """ | |
50605a7f TT |
2388 | if type_obj.tag is None: |
2389 | return None | |
2390 | ||
2391 | if self.type_obj is None: | |
2b7f0378 FD |
2392 | if self.targ1 is not None: |
2393 | if not type_obj.tag.startswith('{}<{}'.format(self.template, self.targ1)): | |
2394 | # Filter didn't match. | |
2395 | return None | |
2396 | elif not type_obj.tag.startswith(self.template): | |
50605a7f TT |
2397 | # Filter didn't match. |
2398 | return None | |
2b7f0378 | 2399 | |
50605a7f TT |
2400 | try: |
2401 | self.type_obj = gdb.lookup_type(self.name).strip_typedefs() | |
2402 | except: | |
2403 | pass | |
4347fea9 FD |
2404 | |
2405 | if self.type_obj is None: | |
2406 | return None | |
2407 | ||
2b7f0378 FD |
2408 | if gdb.types.get_basic_type(self.type_obj) == gdb.types.get_basic_type(type_obj): |
2409 | return strip_inline_namespaces(self.name) | |
4347fea9 | 2410 | |
2b7f0378 FD |
2411 | # Workaround ambiguous typedefs matching both std:: and std::__cxx11:: symbols. |
2412 | if self.template.split('::')[-1] == 'basic_string': | |
4347fea9 FD |
2413 | if self.type_obj.tag.replace('__cxx11::', '') == type_obj.tag.replace('__cxx11::', ''): |
2414 | return strip_inline_namespaces(self.name) | |
2415 | ||
50605a7f TT |
2416 | return None |
2417 | ||
2418 | def instantiate(self): | |
bab0a26d | 2419 | "Return a recognizer object for this type printer." |
2b7f0378 | 2420 | return self._recognizer(self.template, self.name, self.targ1) |
50605a7f | 2421 | |
2b7f0378 FD |
2422 | def add_one_type_printer(obj, template, name, targ1 = None): |
2423 | printer = FilteringTypePrinter('std::' + template, 'std::' + name, targ1) | |
50605a7f | 2424 | gdb.types.register_type_printer(obj, printer) |
2b7f0378 | 2425 | if _versioned_namespace and not '__cxx11' in template: |
bab0a26d | 2426 | ns = 'std::' + _versioned_namespace |
2b7f0378 | 2427 | printer = FilteringTypePrinter(ns + template, ns + name, targ1) |
f9a27859 | 2428 | gdb.types.register_type_printer(obj, printer) |
50605a7f TT |
2429 | |
2430 | def register_type_printers(obj): | |
2431 | global _use_type_printing | |
2432 | ||
2433 | if not _use_type_printing: | |
2434 | return | |
2435 | ||
bab0a26d | 2436 | # Add type printers for typedefs std::string, std::wstring etc. |
2b7f0378 FD |
2437 | for ch in (('', 'char'), |
2438 | ('w', 'wchar_t'), | |
2439 | ('u8', 'char8_t'), | |
2440 | ('u16', 'char16_t'), | |
2441 | ('u32', 'char32_t')): | |
2442 | add_one_type_printer(obj, 'basic_string', ch[0] + 'string', ch[1]) | |
2443 | add_one_type_printer(obj, '__cxx11::basic_string', ch[0] + 'string', ch[1]) | |
33b43b0d | 2444 | # Typedefs for __cxx11::basic_string used to be in namespace __cxx11: |
bab0a26d | 2445 | add_one_type_printer(obj, '__cxx11::basic_string', |
2b7f0378 FD |
2446 | '__cxx11::' + ch[0] + 'string', ch[1]) |
2447 | add_one_type_printer(obj, 'basic_string_view', ch[0] + 'string_view', ch[1]) | |
bab0a26d JW |
2448 | |
2449 | # Add type printers for typedefs std::istream, std::wistream etc. | |
2b7f0378 | 2450 | for ch in (('', 'char'), ('w', 'wchar_t')): |
bab0a26d JW |
2451 | for x in ('ios', 'streambuf', 'istream', 'ostream', 'iostream', |
2452 | 'filebuf', 'ifstream', 'ofstream', 'fstream'): | |
2b7f0378 | 2453 | add_one_type_printer(obj, 'basic_' + x, ch[0] + x, ch[1]) |
bab0a26d JW |
2454 | for x in ('stringbuf', 'istringstream', 'ostringstream', |
2455 | 'stringstream'): | |
2b7f0378 | 2456 | add_one_type_printer(obj, 'basic_' + x, ch[0] + x, ch[1]) |
33b43b0d | 2457 | # <sstream> types are in __cxx11 namespace, but typedefs aren't: |
2b7f0378 | 2458 | add_one_type_printer(obj, '__cxx11::basic_' + x, ch[0] + x, ch[1]) |
bab0a26d JW |
2459 | |
2460 | # Add type printers for typedefs regex, wregex, cmatch, wcmatch etc. | |
2461 | for abi in ('', '__cxx11::'): | |
2b7f0378 FD |
2462 | for ch in (('', 'char'), ('w', 'wchar_t')): |
2463 | add_one_type_printer(obj, abi + 'basic_regex', abi + ch[0] + 'regex', ch[1]) | |
bab0a26d JW |
2464 | for ch in ('c', 's', 'wc', 'ws'): |
2465 | add_one_type_printer(obj, abi + 'match_results', abi + ch + 'match') | |
2466 | for x in ('sub_match', 'regex_iterator', 'regex_token_iterator'): | |
2467 | add_one_type_printer(obj, abi + x, abi + ch + x) | |
50605a7f TT |
2468 | |
2469 | # Note that we can't have a printer for std::wstreampos, because | |
bab0a26d | 2470 | # it is the same type as std::streampos. |
50605a7f | 2471 | add_one_type_printer(obj, 'fpos', 'streampos') |
f9a27859 | 2472 | |
bab0a26d | 2473 | # Add type printers for <chrono> typedefs. |
d33a250f JW |
2474 | for dur in ('nanoseconds', 'microseconds', 'milliseconds', 'seconds', |
2475 | 'minutes', 'hours', 'days', 'weeks', 'years', 'months'): | |
2476 | add_one_type_printer(obj, 'chrono::duration', 'chrono::' + dur) | |
50605a7f | 2477 | |
bab0a26d | 2478 | # Add type printers for <random> typedefs. |
50605a7f TT |
2479 | add_one_type_printer(obj, 'linear_congruential_engine', 'minstd_rand0') |
2480 | add_one_type_printer(obj, 'linear_congruential_engine', 'minstd_rand') | |
2481 | add_one_type_printer(obj, 'mersenne_twister_engine', 'mt19937') | |
2482 | add_one_type_printer(obj, 'mersenne_twister_engine', 'mt19937_64') | |
2483 | add_one_type_printer(obj, 'subtract_with_carry_engine', 'ranlux24_base') | |
2484 | add_one_type_printer(obj, 'subtract_with_carry_engine', 'ranlux48_base') | |
2485 | add_one_type_printer(obj, 'discard_block_engine', 'ranlux24') | |
2486 | add_one_type_printer(obj, 'discard_block_engine', 'ranlux48') | |
2487 | add_one_type_printer(obj, 'shuffle_order_engine', 'knuth_b') | |
2488 | ||
bab0a26d JW |
2489 | # Add type printers for experimental::basic_string_view typedefs. |
2490 | ns = 'experimental::fundamentals_v1::' | |
2b7f0378 FD |
2491 | for ch in (('', 'char'), |
2492 | ('w', 'wchar_t'), | |
2493 | ('u8', 'char8_t'), | |
2494 | ('u16', 'char16_t'), | |
2495 | ('u32', 'char32_t')): | |
bab0a26d | 2496 | add_one_type_printer(obj, ns + 'basic_string_view', |
2b7f0378 | 2497 | ns + ch[0] + 'string_view', ch[1]) |
bab0a26d JW |
2498 | |
2499 | # Do not show defaulted template arguments in class templates. | |
2500 | add_one_template_type_printer(obj, 'unique_ptr', | |
2501 | { 1: 'std::default_delete<{0}>' }) | |
2502 | add_one_template_type_printer(obj, 'deque', { 1: 'std::allocator<{0}>'}) | |
2503 | add_one_template_type_printer(obj, 'forward_list', { 1: 'std::allocator<{0}>'}) | |
2504 | add_one_template_type_printer(obj, 'list', { 1: 'std::allocator<{0}>'}) | |
2505 | add_one_template_type_printer(obj, '__cxx11::list', { 1: 'std::allocator<{0}>'}) | |
2506 | add_one_template_type_printer(obj, 'vector', { 1: 'std::allocator<{0}>'}) | |
2507 | add_one_template_type_printer(obj, 'map', | |
2508 | { 2: 'std::less<{0}>', | |
2509 | 3: 'std::allocator<std::pair<{0} const, {1}>>' }) | |
2510 | add_one_template_type_printer(obj, 'multimap', | |
2511 | { 2: 'std::less<{0}>', | |
2512 | 3: 'std::allocator<std::pair<{0} const, {1}>>' }) | |
2513 | add_one_template_type_printer(obj, 'set', | |
2514 | { 1: 'std::less<{0}>', 2: 'std::allocator<{0}>' }) | |
2515 | add_one_template_type_printer(obj, 'multiset', | |
2516 | { 1: 'std::less<{0}>', 2: 'std::allocator<{0}>' }) | |
2517 | add_one_template_type_printer(obj, 'unordered_map', | |
2518 | { 2: 'std::hash<{0}>', | |
2519 | 3: 'std::equal_to<{0}>', | |
2520 | 4: 'std::allocator<std::pair<{0} const, {1}>>'}) | |
2521 | add_one_template_type_printer(obj, 'unordered_multimap', | |
2522 | { 2: 'std::hash<{0}>', | |
2523 | 3: 'std::equal_to<{0}>', | |
2524 | 4: 'std::allocator<std::pair<{0} const, {1}>>'}) | |
2525 | add_one_template_type_printer(obj, 'unordered_set', | |
2526 | { 1: 'std::hash<{0}>', | |
2527 | 2: 'std::equal_to<{0}>', | |
2528 | 3: 'std::allocator<{0}>'}) | |
2529 | add_one_template_type_printer(obj, 'unordered_multiset', | |
2530 | { 1: 'std::hash<{0}>', | |
2531 | 2: 'std::equal_to<{0}>', | |
2532 | 3: 'std::allocator<{0}>'}) | |
077aa95e | 2533 | |
d63c53cc TT |
2534 | def register_libstdcxx_printers (obj): |
2535 | "Register libstdc++ pretty-printers with objfile Obj." | |
2536 | ||
2537 | global _use_gdb_pp | |
2538 | global libstdcxx_printer | |
2539 | ||
2540 | if _use_gdb_pp: | |
2541 | gdb.printing.register_pretty_printer(obj, libstdcxx_printer) | |
2542 | else: | |
2543 | if obj is None: | |
2544 | obj = gdb | |
2545 | obj.pretty_printers.append(libstdcxx_printer) | |
41850419 | 2546 | |
50605a7f TT |
2547 | register_type_printers(obj) |
2548 | ||
41850419 | 2549 | def build_libstdcxx_dictionary (): |
d63c53cc TT |
2550 | global libstdcxx_printer |
2551 | ||
2552 | libstdcxx_printer = Printer("libstdc++-v6") | |
2553 | ||
41850419 TT |
2554 | # libstdc++ objects requiring pretty-printing. |
2555 | # In order from: | |
2556 | # http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/a01847.html | |
3efe2bf7 | 2557 | libstdcxx_printer.add_version('std::', 'basic_string', StdStringPrinter) |
86397ed1 | 2558 | libstdcxx_printer.add_version('std::__cxx11::', 'basic_string', StdStringPrinter) |
3efe2bf7 TT |
2559 | libstdcxx_printer.add_container('std::', 'bitset', StdBitsetPrinter) |
2560 | libstdcxx_printer.add_container('std::', 'deque', StdDequePrinter) | |
2561 | libstdcxx_printer.add_container('std::', 'list', StdListPrinter) | |
bf1de1ac | 2562 | libstdcxx_printer.add_container('std::__cxx11::', 'list', StdListPrinter) |
3efe2bf7 TT |
2563 | libstdcxx_printer.add_container('std::', 'map', StdMapPrinter) |
2564 | libstdcxx_printer.add_container('std::', 'multimap', StdMapPrinter) | |
2565 | libstdcxx_printer.add_container('std::', 'multiset', StdSetPrinter) | |
e1821582 | 2566 | libstdcxx_printer.add_version('std::', 'pair', StdPairPrinter) |
3efe2bf7 TT |
2567 | libstdcxx_printer.add_version('std::', 'priority_queue', |
2568 | StdStackOrQueuePrinter) | |
2569 | libstdcxx_printer.add_version('std::', 'queue', StdStackOrQueuePrinter) | |
2570 | libstdcxx_printer.add_version('std::', 'tuple', StdTuplePrinter) | |
2571 | libstdcxx_printer.add_container('std::', 'set', StdSetPrinter) | |
2572 | libstdcxx_printer.add_version('std::', 'stack', StdStackOrQueuePrinter) | |
2573 | libstdcxx_printer.add_version('std::', 'unique_ptr', UniquePointerPrinter) | |
2574 | libstdcxx_printer.add_container('std::', 'vector', StdVectorPrinter) | |
41850419 TT |
2575 | # vector<bool> |
2576 | ||
36100e0e JW |
2577 | if hasattr(gdb.Value, 'dynamic_type'): |
2578 | libstdcxx_printer.add_version('std::', 'error_code', | |
2579 | StdErrorCodePrinter) | |
2580 | libstdcxx_printer.add_version('std::', 'error_condition', | |
2581 | StdErrorCodePrinter) | |
2582 | ||
cd0961a5 | 2583 | # Printer registrations for classes compiled with -D_GLIBCXX_DEBUG. |
d63c53cc TT |
2584 | libstdcxx_printer.add('std::__debug::bitset', StdBitsetPrinter) |
2585 | libstdcxx_printer.add('std::__debug::deque', StdDequePrinter) | |
2586 | libstdcxx_printer.add('std::__debug::list', StdListPrinter) | |
2587 | libstdcxx_printer.add('std::__debug::map', StdMapPrinter) | |
2588 | libstdcxx_printer.add('std::__debug::multimap', StdMapPrinter) | |
2589 | libstdcxx_printer.add('std::__debug::multiset', StdSetPrinter) | |
d63c53cc | 2590 | libstdcxx_printer.add('std::__debug::set', StdSetPrinter) |
d63c53cc | 2591 | libstdcxx_printer.add('std::__debug::vector', StdVectorPrinter) |
cd0961a5 | 2592 | |
00895372 | 2593 | # These are the TR1 and C++11 printers. |
41850419 | 2594 | # For array - the default GDB pretty-printer seems reasonable. |
a1527f2f JW |
2595 | libstdcxx_printer.add_version('std::', 'shared_ptr', SharedPointerPrinter) |
2596 | libstdcxx_printer.add_version('std::', 'weak_ptr', SharedPointerPrinter) | |
3efe2bf7 TT |
2597 | libstdcxx_printer.add_container('std::', 'unordered_map', |
2598 | Tr1UnorderedMapPrinter) | |
2599 | libstdcxx_printer.add_container('std::', 'unordered_set', | |
2600 | Tr1UnorderedSetPrinter) | |
2601 | libstdcxx_printer.add_container('std::', 'unordered_multimap', | |
2602 | Tr1UnorderedMapPrinter) | |
2603 | libstdcxx_printer.add_container('std::', 'unordered_multiset', | |
2604 | Tr1UnorderedSetPrinter) | |
2605 | libstdcxx_printer.add_container('std::', 'forward_list', | |
2606 | StdForwardListPrinter) | |
2607 | ||
86397ed1 FD |
2608 | libstdcxx_printer.add_version('std::tr1::', 'shared_ptr', SharedPointerPrinter) |
2609 | libstdcxx_printer.add_version('std::tr1::', 'weak_ptr', SharedPointerPrinter) | |
2610 | libstdcxx_printer.add_version('std::tr1::', 'unordered_map', | |
3efe2bf7 | 2611 | Tr1UnorderedMapPrinter) |
86397ed1 | 2612 | libstdcxx_printer.add_version('std::tr1::', 'unordered_set', |
3efe2bf7 | 2613 | Tr1UnorderedSetPrinter) |
86397ed1 | 2614 | libstdcxx_printer.add_version('std::tr1::', 'unordered_multimap', |
3efe2bf7 | 2615 | Tr1UnorderedMapPrinter) |
86397ed1 | 2616 | libstdcxx_printer.add_version('std::tr1::', 'unordered_multiset', |
3efe2bf7 | 2617 | Tr1UnorderedSetPrinter) |
41850419 | 2618 | |
71999fde PF |
2619 | libstdcxx_printer.add_version('std::', 'initializer_list', |
2620 | StdInitializerListPrinter) | |
a8495845 | 2621 | libstdcxx_printer.add_version('std::', 'atomic', StdAtomicPrinter) |
93257ed6 PF |
2622 | libstdcxx_printer.add_version('std::', 'basic_stringbuf', StdStringBufPrinter) |
2623 | libstdcxx_printer.add_version('std::__cxx11::', 'basic_stringbuf', StdStringBufPrinter) | |
2624 | for sstream in ('istringstream', 'ostringstream', 'stringstream'): | |
2625 | libstdcxx_printer.add_version('std::', 'basic_' + sstream, StdStringStreamPrinter) | |
2626 | libstdcxx_printer.add_version('std::__cxx11::', 'basic_' + sstream, StdStringStreamPrinter) | |
71999fde | 2627 | |
d33a250f JW |
2628 | libstdcxx_printer.add_version('std::chrono::', 'duration', |
2629 | StdChronoDurationPrinter) | |
2630 | libstdcxx_printer.add_version('std::chrono::', 'time_point', | |
2631 | StdChronoTimePointPrinter) | |
2632 | ||
7a2f2d91 JW |
2633 | # std::regex components |
2634 | libstdcxx_printer.add_version('std::__detail::', '_State', | |
2635 | StdRegexStatePrinter) | |
2636 | ||
00895372 | 2637 | # These are the C++11 printer registrations for -D_GLIBCXX_DEBUG cases. |
3c760f4a JW |
2638 | # The tr1 namespace containers do not have any debug equivalents, |
2639 | # so do not register printers for them. | |
d63c53cc TT |
2640 | libstdcxx_printer.add('std::__debug::unordered_map', |
2641 | Tr1UnorderedMapPrinter) | |
2642 | libstdcxx_printer.add('std::__debug::unordered_set', | |
2643 | Tr1UnorderedSetPrinter) | |
2644 | libstdcxx_printer.add('std::__debug::unordered_multimap', | |
2645 | Tr1UnorderedMapPrinter) | |
2646 | libstdcxx_printer.add('std::__debug::unordered_multiset', | |
2647 | Tr1UnorderedSetPrinter) | |
8dfb08ab JW |
2648 | libstdcxx_printer.add('std::__debug::forward_list', |
2649 | StdForwardListPrinter) | |
cd0961a5 | 2650 | |
6f440cce JW |
2651 | # Library Fundamentals TS components |
2652 | libstdcxx_printer.add_version('std::experimental::fundamentals_v1::', | |
2653 | 'any', StdExpAnyPrinter) | |
2654 | libstdcxx_printer.add_version('std::experimental::fundamentals_v1::', | |
2655 | 'optional', StdExpOptionalPrinter) | |
2656 | libstdcxx_printer.add_version('std::experimental::fundamentals_v1::', | |
2657 | 'basic_string_view', StdExpStringViewPrinter) | |
0ca7ba9a JW |
2658 | # Filesystem TS components |
2659 | libstdcxx_printer.add_version('std::experimental::filesystem::v1::', | |
2660 | 'path', StdExpPathPrinter) | |
a00d74c4 | 2661 | libstdcxx_printer.add_version('std::experimental::filesystem::v1::__cxx11::', |
0ca7ba9a | 2662 | 'path', StdExpPathPrinter) |
641cb5a6 | 2663 | libstdcxx_printer.add_version('std::filesystem::', |
4f87bb8d | 2664 | 'path', StdPathPrinter) |
641cb5a6 | 2665 | libstdcxx_printer.add_version('std::filesystem::__cxx11::', |
4f87bb8d | 2666 | 'path', StdPathPrinter) |
41850419 | 2667 | |
00895372 JW |
2668 | # C++17 components |
2669 | libstdcxx_printer.add_version('std::', | |
2670 | 'any', StdExpAnyPrinter) | |
2671 | libstdcxx_printer.add_version('std::', | |
2672 | 'optional', StdExpOptionalPrinter) | |
2673 | libstdcxx_printer.add_version('std::', | |
2674 | 'basic_string_view', StdExpStringViewPrinter) | |
2675 | libstdcxx_printer.add_version('std::', | |
2676 | 'variant', StdVariantPrinter) | |
2dbe56bd JW |
2677 | libstdcxx_printer.add_version('std::', |
2678 | '_Node_handle', StdNodeHandlePrinter) | |
00895372 | 2679 | |
a59c50bd JW |
2680 | # C++20 components |
2681 | libstdcxx_printer.add_version('std::', 'partial_ordering', StdCmpCatPrinter) | |
2682 | libstdcxx_printer.add_version('std::', 'weak_ordering', StdCmpCatPrinter) | |
2683 | libstdcxx_printer.add_version('std::', 'strong_ordering', StdCmpCatPrinter) | |
fdb3f82f | 2684 | libstdcxx_printer.add_version('std::', 'span', StdSpanPrinter) |
d33a250f JW |
2685 | libstdcxx_printer.add_version('std::', 'basic_format_args', |
2686 | StdFormatArgsPrinter) | |
2687 | for c in ['day','month','year','weekday','weekday_indexed','weekday_last', | |
2688 | 'month_day','month_day_last','month_weekday','month_weekday_last', | |
2689 | 'year_month','year_month_day','year_month_day_last', | |
2690 | 'year_month_weekday','year_month_weekday_last', 'hh_mm_ss']: | |
2691 | libstdcxx_printer.add_version('std::chrono::', c, | |
2692 | StdChronoCalendarPrinter) | |
2693 | libstdcxx_printer.add_version('std::chrono::', 'time_zone', | |
2694 | StdChronoTimeZonePrinter) | |
2695 | libstdcxx_printer.add_version('std::chrono::', 'time_zone_link', | |
2696 | StdChronoTimeZonePrinter) | |
2697 | libstdcxx_printer.add_version('std::chrono::', 'zoned_time', | |
2698 | StdChronoZonedTimePrinter) | |
2699 | libstdcxx_printer.add_version('std::chrono::', 'leap_second', | |
2700 | StdChronoLeapSecondPrinter) | |
2701 | libstdcxx_printer.add_version('std::chrono::', 'tzdb', StdChronoTzdbPrinter) | |
2702 | #libstdcxx_printer.add_version('std::chrono::(anonymous namespace)', 'Rule', | |
2703 | # StdChronoTimeZoneRulePrinter) | |
a59c50bd | 2704 | |
41850419 | 2705 | # Extensions. |
3efe2bf7 | 2706 | libstdcxx_printer.add_version('__gnu_cxx::', 'slist', StdSlistPrinter) |
41850419 TT |
2707 | |
2708 | if True: | |
2709 | # These shouldn't be necessary, if GDB "print *i" worked. | |
2710 | # But it often doesn't, so here they are. | |
3efe2bf7 TT |
2711 | libstdcxx_printer.add_container('std::', '_List_iterator', |
2712 | StdListIteratorPrinter) | |
2713 | libstdcxx_printer.add_container('std::', '_List_const_iterator', | |
2714 | StdListIteratorPrinter) | |
2715 | libstdcxx_printer.add_version('std::', '_Rb_tree_iterator', | |
2716 | StdRbtreeIteratorPrinter) | |
2717 | libstdcxx_printer.add_version('std::', '_Rb_tree_const_iterator', | |
2718 | StdRbtreeIteratorPrinter) | |
2719 | libstdcxx_printer.add_container('std::', '_Deque_iterator', | |
2720 | StdDequeIteratorPrinter) | |
2721 | libstdcxx_printer.add_container('std::', '_Deque_const_iterator', | |
2722 | StdDequeIteratorPrinter) | |
2723 | libstdcxx_printer.add_version('__gnu_cxx::', '__normal_iterator', | |
2724 | StdVectorIteratorPrinter) | |
c883d1dc | 2725 | libstdcxx_printer.add_container('std::', '_Bit_iterator', |
39836f83 | 2726 | StdBitIteratorPrinter) |
c883d1dc | 2727 | libstdcxx_printer.add_container('std::', '_Bit_const_iterator', |
39836f83 | 2728 | StdBitIteratorPrinter) |
c883d1dc | 2729 | libstdcxx_printer.add_container('std::', '_Bit_reference', |
39836f83 | 2730 | StdBitReferencePrinter) |
3efe2bf7 TT |
2731 | libstdcxx_printer.add_version('__gnu_cxx::', '_Slist_iterator', |
2732 | StdSlistIteratorPrinter) | |
5da6b013 FD |
2733 | libstdcxx_printer.add_container('std::', '_Fwd_list_iterator', |
2734 | StdFwdListIteratorPrinter) | |
2735 | libstdcxx_printer.add_container('std::', '_Fwd_list_const_iterator', | |
2736 | StdFwdListIteratorPrinter) | |
d63c53cc TT |
2737 | |
2738 | # Debug (compiled with -D_GLIBCXX_DEBUG) printer | |
fe6bd21a | 2739 | # registrations. |
d63c53cc TT |
2740 | libstdcxx_printer.add('__gnu_debug::_Safe_iterator', |
2741 | StdDebugIteratorPrinter) | |
41850419 TT |
2742 | |
2743 | build_libstdcxx_dictionary () |