mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2025-01-12 12:16:04 +08:00
c973d0aa4a
(Ref: https://sourceware.org/ml/gdb/2017-06/msg00020.html) Assuming int_t is a typedef to int: typedef int int_t; gdb currently loses this expression's typedef: (gdb) p (int_t) 0 $1 = 0 (gdb) whatis $1 type = int or: (gdb) whatis (int_t) 0 type = int or, to get "whatis" out of the way: (gdb) maint print type (int_t) 0 ... name 'int' code 0x8 (TYPE_CODE_INT) ... This prevents a type printer for "int_t" kicking in, with e.g.: (gdb) p (int_t) 0 From the manual, we can see that that "whatis (int_t) 0" command invocation should have printed "type = int_t": If @var{arg} is a variable or an expression, @code{whatis} prints its literal type as it is used in the source code. If the type was defined using a @code{typedef}, @code{whatis} will @emph{not} print the data type underlying the @code{typedef}. (...) If @var{arg} is a type name that was defined using @code{typedef}, @code{whatis} @dfn{unrolls} only one level of that @code{typedef}. That one-level stripping is currently done here, in gdb/eval.c:evaluate_subexp_standard, handling OP_TYPE: ... else if (noside == EVAL_AVOID_SIDE_EFFECTS) { struct type *type = exp->elts[pc + 1].type; /* If this is a typedef, then find its immediate target. We use check_typedef to resolve stubs, but we ignore its result because we do not want to dig past all typedefs. */ check_typedef (type); if (TYPE_CODE (type) == TYPE_CODE_TYPEDEF) type = TYPE_TARGET_TYPE (type); return allocate_value (type); } However, this stripping is reachable in both: #1 - (gdb) whatis (int_t)0 # ARG is an expression with a cast to # typedef type. #2 - (gdb) whatis int_t # ARG is a type name. while only case #2 should strip the typedef. Removing that code from evaluate_subexp_standard is part of the fix. Instead, we make the "whatis" command implementation itself strip one level of typedefs when the command argument is a type name. We then run into another problem, also fixed by this commit: value_cast always drops any typedefs of the destination type. With all that fixed, "whatis (int_t) 0" now works as expected: (gdb) whatis int_t type = int (gdb) whatis (int_t)0 type = int_t value_cast has many different exit/convertion paths, for handling many different kinds of casts/conversions, and most of them had to be tweaked to construct the value of the right "to" type. The new tests try to exercise most of it, by trying castin of many different combinations of types. With: $ make check TESTS="*/whatis-ptype*.exp */gnu_vector.exp */dfp-test.exp" ... due to combinatorial explosion, the testsuite results for the tests above alone grow like: - # of expected passes 246 + # of expected passes 3811 You'll note that the tests exposed one GCC buglet, filed here: Missing DW_AT_type in DW_TAG_typedef of "typedef of typedef of void" https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81267 gdb/ChangeLog: 2017-08-21 Pedro Alves <palves@redhat.com> * eval.c (evaluate_subexp_standard) <OP_TYPE>: Don't dig past typedefs. * typeprint.c (whatis_exp): If handling "whatis", and expression is OP_TYPE, strip one typedef level. Otherwise don't strip typedefs here. * valops.c (value_cast): Save "to" type before resolving stubs/typedefs. Use that type as resulting value's type. gdb/testsuite/ChangeLog: 2017-08-21 Pedro Alves <palves@redhat.com> * gdb.base/dfp-test.c (d32_t, d64_t, d128_t, d32_t2, d64_t2, d128_t2, v_d32_t, v_d64_t) (v_d128_t, v_d32_t2, v_d64_t2, v_d128_t2): New. * gdb.base/dfp-test.exp: Add whatis/ptype/cast tests. * gdb.base/gnu_vector.exp: Add whatis/ptype/cast tests. * gdb.base/whatis-ptype-typedefs.c: New. * gdb.base/whatis-ptype-typedefs.exp: New. * gdb.python/py-prettyprint.c (int_type, int_type2): New typedefs. (an_int, an_int_type, an_int_type2): New globals. * gdb.python/py-prettyprint.exp (run_lang_tests): Add tests involving typedefs and cast expressions. * gdb.python/py-prettyprint.py (class pp_int_typedef): New. (lookup_typedefs_function): New. (typedefs_pretty_printers_dict): New. (top level): Register lookup_typedefs_function in gdb.pretty_printers.
350 lines
10 KiB
Python
350 lines
10 KiB
Python
# Copyright (C) 2008-2017 Free Software Foundation, Inc.
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
# This file is part of the GDB testsuite. It tests python pretty
|
|
# printers.
|
|
|
|
import re
|
|
import gdb
|
|
|
|
def _iterator (pointer, len):
|
|
start = pointer
|
|
end = pointer + len
|
|
while pointer != end:
|
|
yield ('[%d]' % int (pointer - start), pointer.dereference())
|
|
pointer += 1
|
|
|
|
# Same as _iterator but can be told to raise an exception.
|
|
def _iterator_except (pointer, len):
|
|
start = pointer
|
|
end = pointer + len
|
|
while pointer != end:
|
|
if exception_flag:
|
|
raise gdb.MemoryError ('hi bob')
|
|
yield ('[%d]' % int (pointer - start), pointer.dereference())
|
|
pointer += 1
|
|
|
|
# Test returning a Value from a printer.
|
|
class string_print (object):
|
|
def __init__(self, val):
|
|
self.val = val
|
|
|
|
def to_string(self):
|
|
return self.val['whybother']['contents']
|
|
|
|
# Test a class-based printer.
|
|
class ContainerPrinter (object):
|
|
|
|
def __init__(self, val):
|
|
self.val = val
|
|
|
|
def to_string(self):
|
|
return 'container %s with %d elements' % (self.val['name'], self.val['len'])
|
|
|
|
def children(self):
|
|
return _iterator(self.val['elements'], self.val['len'])
|
|
|
|
# Treats a container as array.
|
|
class ArrayPrinter (object):
|
|
def __init__(self, val):
|
|
self.val = val
|
|
|
|
def to_string(self):
|
|
return 'array %s with %d elements' % (self.val['name'], self.val['len'])
|
|
|
|
def children(self):
|
|
return _iterator(self.val['elements'], self.val['len'])
|
|
|
|
def display_hint (self):
|
|
return 'array'
|
|
|
|
# Flag to make NoStringContainerPrinter throw an exception.
|
|
exception_flag = False
|
|
|
|
# Test a printer where to_string is None
|
|
class NoStringContainerPrinter (object):
|
|
def __init__(self, val):
|
|
self.val = val
|
|
|
|
def to_string(self):
|
|
return None
|
|
|
|
def children(self):
|
|
return _iterator_except (self.val['elements'], self.val['len'])
|
|
|
|
class pp_s (object):
|
|
def __init__(self, val):
|
|
self.val = val
|
|
|
|
def to_string(self):
|
|
a = self.val["a"]
|
|
b = self.val["b"]
|
|
if a.address != b:
|
|
raise Exception("&a(%s) != b(%s)" % (str(a.address), str(b)))
|
|
return " a=<" + str(self.val["a"]) + "> b=<" + str(self.val["b"]) + ">"
|
|
|
|
class pp_ss (object):
|
|
def __init__(self, val):
|
|
self.val = val
|
|
|
|
def to_string(self):
|
|
return "a=<" + str(self.val["a"]) + "> b=<" + str(self.val["b"]) + ">"
|
|
|
|
class pp_sss (object):
|
|
def __init__(self, val):
|
|
self.val = val
|
|
|
|
def to_string(self):
|
|
return "a=<" + str(self.val['a']) + "> b=<" + str(self.val["b"]) + ">"
|
|
|
|
class pp_multiple_virtual (object):
|
|
def __init__ (self, val):
|
|
self.val = val
|
|
|
|
def to_string (self):
|
|
return "pp value variable is: " + str (self.val['value'])
|
|
|
|
class pp_vbase1 (object):
|
|
def __init__ (self, val):
|
|
self.val = val
|
|
|
|
def to_string (self):
|
|
return "pp class name: " + self.val.type.tag
|
|
|
|
class pp_nullstr (object):
|
|
def __init__(self, val):
|
|
self.val = val
|
|
|
|
def to_string(self):
|
|
return self.val['s'].string(gdb.target_charset())
|
|
|
|
class pp_ns (object):
|
|
"Print a std::basic_string of some kind"
|
|
|
|
def __init__(self, val):
|
|
self.val = val
|
|
|
|
def to_string(self):
|
|
len = self.val['length']
|
|
return self.val['null_str'].string (gdb.target_charset(), length = len)
|
|
|
|
def display_hint (self):
|
|
return 'string'
|
|
|
|
pp_ls_encoding = None
|
|
|
|
class pp_ls (object):
|
|
"Print a std::basic_string of some kind"
|
|
|
|
def __init__(self, val):
|
|
self.val = val
|
|
|
|
def to_string(self):
|
|
length = self.val['len']
|
|
if pp_ls_encoding is not None:
|
|
if length >= 0:
|
|
return self.val['lazy_str'].lazy_string(
|
|
encoding = pp_ls_encoding,
|
|
length = length)
|
|
else:
|
|
return self.val['lazy_str'].lazy_string(
|
|
encoding = pp_ls_encoding)
|
|
else:
|
|
if length >= 0:
|
|
return self.val['lazy_str'].lazy_string(length = length)
|
|
else:
|
|
return self.val['lazy_str'].lazy_string()
|
|
|
|
def display_hint (self):
|
|
return 'string'
|
|
|
|
class pp_hint_error (object):
|
|
"Throw error from display_hint"
|
|
|
|
def __init__(self, val):
|
|
self.val = val
|
|
|
|
def to_string(self):
|
|
return 'hint_error_val'
|
|
|
|
def display_hint (self):
|
|
raise Exception("hint failed")
|
|
|
|
class pp_children_as_list (object):
|
|
"Throw error from display_hint"
|
|
|
|
def __init__(self, val):
|
|
self.val = val
|
|
|
|
def to_string(self):
|
|
return 'children_as_list_val'
|
|
|
|
def children (self):
|
|
return [('one', 1)]
|
|
|
|
class pp_outer (object):
|
|
"Print struct outer"
|
|
|
|
def __init__ (self, val):
|
|
self.val = val
|
|
|
|
def to_string (self):
|
|
return "x = %s" % self.val['x']
|
|
|
|
def children (self):
|
|
yield 's', self.val['s']
|
|
yield 'x', self.val['x']
|
|
|
|
class MemoryErrorString (object):
|
|
"Raise an error"
|
|
|
|
def __init__(self, val):
|
|
self.val = val
|
|
|
|
def to_string(self):
|
|
raise gdb.MemoryError ("Cannot access memory.")
|
|
|
|
def display_hint (self):
|
|
return 'string'
|
|
|
|
class pp_eval_type (object):
|
|
def __init__(self, val):
|
|
self.val = val
|
|
|
|
def to_string(self):
|
|
gdb.execute("bt", to_string=True)
|
|
return "eval=<" + str(gdb.parse_and_eval("eval_func (123456789, 2, 3, 4, 5, 6, 7, 8)")) + ">"
|
|
|
|
class pp_int_typedef (object):
|
|
def __init__(self, val):
|
|
self.val = val
|
|
|
|
def to_string(self):
|
|
return "type=%s, val=%s" % (self.val.type, int(self.val))
|
|
|
|
def lookup_function (val):
|
|
"Look-up and return a pretty-printer that can print val."
|
|
|
|
# Get the type.
|
|
type = val.type
|
|
|
|
# If it points to a reference, get the reference.
|
|
if type.code == gdb.TYPE_CODE_REF:
|
|
type = type.target ()
|
|
|
|
# Get the unqualified type, stripped of typedefs.
|
|
type = type.unqualified ().strip_typedefs ()
|
|
|
|
# Get the type name.
|
|
typename = type.tag
|
|
|
|
if typename == None:
|
|
return None
|
|
|
|
# Iterate over local dictionary of types to determine
|
|
# if a printer is registered for that type. Return an
|
|
# instantiation of the printer if found.
|
|
for function in pretty_printers_dict:
|
|
if function.match (typename):
|
|
return pretty_printers_dict[function] (val)
|
|
|
|
# Cannot find a pretty printer. Return None.
|
|
|
|
return None
|
|
|
|
def disable_lookup_function ():
|
|
lookup_function.enabled = False
|
|
|
|
def enable_lookup_function ():
|
|
lookup_function.enabled = True
|
|
|
|
# Lookup a printer for VAL in the typedefs dict.
|
|
def lookup_typedefs_function (val):
|
|
"Look-up and return a pretty-printer that can print val (typedefs)."
|
|
|
|
# Get the type.
|
|
type = val.type
|
|
|
|
if type == None or type.name == None or type.code != gdb.TYPE_CODE_TYPEDEF:
|
|
return None
|
|
|
|
# Iterate over local dictionary of typedef types to determine if a
|
|
# printer is registered for that type. Return an instantiation of
|
|
# the printer if found.
|
|
for function in typedefs_pretty_printers_dict:
|
|
if function.match (type.name):
|
|
return typedefs_pretty_printers_dict[function] (val)
|
|
|
|
# Cannot find a pretty printer.
|
|
return None
|
|
|
|
def register_pretty_printers ():
|
|
pretty_printers_dict[re.compile ('^struct s$')] = pp_s
|
|
pretty_printers_dict[re.compile ('^s$')] = pp_s
|
|
pretty_printers_dict[re.compile ('^S$')] = pp_s
|
|
|
|
pretty_printers_dict[re.compile ('^struct ss$')] = pp_ss
|
|
pretty_printers_dict[re.compile ('^ss$')] = pp_ss
|
|
pretty_printers_dict[re.compile ('^const S &$')] = pp_s
|
|
pretty_printers_dict[re.compile ('^SSS$')] = pp_sss
|
|
|
|
pretty_printers_dict[re.compile ('^VirtualTest$')] = pp_multiple_virtual
|
|
pretty_printers_dict[re.compile ('^Vbase1$')] = pp_vbase1
|
|
|
|
pretty_printers_dict[re.compile ('^struct nullstr$')] = pp_nullstr
|
|
pretty_printers_dict[re.compile ('^nullstr$')] = pp_nullstr
|
|
|
|
# Note that we purposely omit the typedef names here.
|
|
# Printer lookup is based on canonical name.
|
|
# However, we do need both tagged and untagged variants, to handle
|
|
# both the C and C++ cases.
|
|
pretty_printers_dict[re.compile ('^struct string_repr$')] = string_print
|
|
pretty_printers_dict[re.compile ('^struct container$')] = ContainerPrinter
|
|
pretty_printers_dict[re.compile ('^struct justchildren$')] = NoStringContainerPrinter
|
|
pretty_printers_dict[re.compile ('^string_repr$')] = string_print
|
|
pretty_printers_dict[re.compile ('^container$')] = ContainerPrinter
|
|
pretty_printers_dict[re.compile ('^justchildren$')] = NoStringContainerPrinter
|
|
|
|
pretty_printers_dict[re.compile ('^struct ns$')] = pp_ns
|
|
pretty_printers_dict[re.compile ('^ns$')] = pp_ns
|
|
|
|
pretty_printers_dict[re.compile ('^struct lazystring$')] = pp_ls
|
|
pretty_printers_dict[re.compile ('^lazystring$')] = pp_ls
|
|
|
|
pretty_printers_dict[re.compile ('^struct outerstruct$')] = pp_outer
|
|
pretty_printers_dict[re.compile ('^outerstruct$')] = pp_outer
|
|
|
|
pretty_printers_dict[re.compile ('^struct hint_error$')] = pp_hint_error
|
|
pretty_printers_dict[re.compile ('^hint_error$')] = pp_hint_error
|
|
|
|
pretty_printers_dict[re.compile ('^struct children_as_list$')] = pp_children_as_list
|
|
pretty_printers_dict[re.compile ('^children_as_list$')] = pp_children_as_list
|
|
|
|
pretty_printers_dict[re.compile ('^memory_error$')] = MemoryErrorString
|
|
|
|
pretty_printers_dict[re.compile ('^eval_type_s$')] = pp_eval_type
|
|
|
|
typedefs_pretty_printers_dict[re.compile ('^int_type$')] = pp_int_typedef
|
|
typedefs_pretty_printers_dict[re.compile ('^int_type2$')] = pp_int_typedef
|
|
|
|
# Dict for struct types with typedefs fully stripped.
|
|
pretty_printers_dict = {}
|
|
# Dict for typedef types.
|
|
typedefs_pretty_printers_dict = {}
|
|
|
|
register_pretty_printers ()
|
|
gdb.pretty_printers.append (lookup_function)
|
|
gdb.pretty_printers.append (lookup_typedefs_function)
|