xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/python/lib/gdb/command/explore.py (revision 8b657b0747480f8989760d71343d6dd33f8d4cf9)
1# GDB 'explore' command.
2# Copyright (C) 2012-2023 Free Software Foundation, Inc.
3
4# This program is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 3 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17"""Implementation of the GDB 'explore' command using the GDB Python API."""
18
19import gdb
20import sys
21
22
23class Explorer(object):
24    """Internal class which invokes other explorers."""
25
26    # This map is filled by the Explorer.init_env() function
27    type_code_to_explorer_map = {}
28
29    _SCALAR_TYPE_LIST = (
30        gdb.TYPE_CODE_CHAR,
31        gdb.TYPE_CODE_INT,
32        gdb.TYPE_CODE_BOOL,
33        gdb.TYPE_CODE_FLT,
34        gdb.TYPE_CODE_VOID,
35        gdb.TYPE_CODE_ENUM,
36    )
37
38    @staticmethod
39    def guard_expr(expr):
40        length = len(expr)
41        guard = False
42
43        if expr[0] == "(" and expr[length - 1] == ")":
44            pass
45        else:
46            i = 0
47            while i < length:
48                c = expr[i]
49                if (
50                    c == "_"
51                    or ("a" <= c and c <= "z")
52                    or ("A" <= c and c <= "Z")
53                    or ("0" <= c and c <= "9")
54                ):
55                    pass
56                else:
57                    guard = True
58                    break
59                i += 1
60
61        if guard:
62            return "(" + expr + ")"
63        else:
64            return expr
65
66    @staticmethod
67    def explore_expr(expr, value, is_child):
68        """Main function to explore an expression value.
69
70        Arguments:
71            expr: The expression string that is being explored.
72            value: The gdb.Value value of the expression.
73            is_child: Boolean value to indicate if the expression is a child.
74                      An expression is a child if it is derived from the main
75                      expression entered by the user.  For example, if the user
76                      entered an expression which evaluates to a struct, then
77                      when exploring the fields of the struct, is_child is set
78                      to True internally.
79
80        Returns:
81            No return value.
82        """
83        type_code = value.type.code
84        if type_code in Explorer.type_code_to_explorer_map:
85            explorer_class = Explorer.type_code_to_explorer_map[type_code]
86            while explorer_class.explore_expr(expr, value, is_child):
87                pass
88        else:
89            print("Explorer for type '%s' not yet available.\n" % str(value.type))
90
91    @staticmethod
92    def explore_type(name, datatype, is_child):
93        """Main function to explore a data type.
94
95        Arguments:
96            name: The string representing the path to the data type being
97                  explored.
98            datatype: The gdb.Type value of the data type being explored.
99            is_child: Boolean value to indicate if the name is a child.
100                      A name is a child if it is derived from the main name
101                      entered by the user.  For example, if the user entered
102                      the name of struct type, then when exploring the fields
103                      of the struct, is_child is set to True internally.
104
105        Returns:
106            No return value.
107        """
108        type_code = datatype.code
109        if type_code in Explorer.type_code_to_explorer_map:
110            explorer_class = Explorer.type_code_to_explorer_map[type_code]
111            while explorer_class.explore_type(name, datatype, is_child):
112                pass
113        else:
114            print("Explorer for type '%s' not yet available.\n" % str(datatype))
115
116    @staticmethod
117    def init_env():
118        """Initializes the Explorer environment.
119        This function should be invoked before starting any exploration.  If
120        invoked before an exploration, it need not be invoked for subsequent
121        explorations.
122        """
123        Explorer.type_code_to_explorer_map = {
124            gdb.TYPE_CODE_CHAR: ScalarExplorer,
125            gdb.TYPE_CODE_INT: ScalarExplorer,
126            gdb.TYPE_CODE_BOOL: ScalarExplorer,
127            gdb.TYPE_CODE_FLT: ScalarExplorer,
128            gdb.TYPE_CODE_VOID: ScalarExplorer,
129            gdb.TYPE_CODE_ENUM: ScalarExplorer,
130            gdb.TYPE_CODE_STRUCT: CompoundExplorer,
131            gdb.TYPE_CODE_UNION: CompoundExplorer,
132            gdb.TYPE_CODE_PTR: PointerExplorer,
133            gdb.TYPE_CODE_REF: ReferenceExplorer,
134            gdb.TYPE_CODE_RVALUE_REF: ReferenceExplorer,
135            gdb.TYPE_CODE_TYPEDEF: TypedefExplorer,
136            gdb.TYPE_CODE_ARRAY: ArrayExplorer,
137        }
138
139    @staticmethod
140    def is_scalar_type(type):
141        """Checks whether a type is a scalar type.
142        A type is a scalar type of its type is
143            gdb.TYPE_CODE_CHAR or
144            gdb.TYPE_CODE_INT or
145            gdb.TYPE_CODE_BOOL or
146            gdb.TYPE_CODE_FLT or
147            gdb.TYPE_CODE_VOID or
148            gdb.TYPE_CODE_ENUM.
149
150        Arguments:
151            type: The type to be checked.
152
153        Returns:
154            'True' if 'type' is a scalar type. 'False' otherwise.
155        """
156        return type.code in Explorer._SCALAR_TYPE_LIST
157
158    @staticmethod
159    def return_to_parent_value():
160        """A utility function which prints that the current exploration session
161        is returning to the parent value. Useful when exploring values.
162        """
163        print("\nReturning to parent value...\n")
164
165    @staticmethod
166    def return_to_parent_value_prompt():
167        """A utility function which prompts the user to press the 'enter' key
168        so that the exploration session can shift back to the parent value.
169        Useful when exploring values.
170        """
171        input("\nPress enter to return to parent value: ")
172
173    @staticmethod
174    def return_to_enclosing_type():
175        """A utility function which prints that the current exploration session
176        is returning to the enclosing type.  Useful when exploring types.
177        """
178        print("\nReturning to enclosing type...\n")
179
180    @staticmethod
181    def return_to_enclosing_type_prompt():
182        """A utility function which prompts the user to press the 'enter' key
183        so that the exploration session can shift back to the enclosing type.
184        Useful when exploring types.
185        """
186        input("\nPress enter to return to enclosing type: ")
187
188
189class ScalarExplorer(object):
190    """Internal class used to explore scalar values."""
191
192    @staticmethod
193    def explore_expr(expr, value, is_child):
194        """Function to explore scalar values.
195        See Explorer.explore_expr and Explorer.is_scalar_type for more
196        information.
197        """
198        print("'%s' is a scalar value of type '%s'." % (expr, value.type))
199        print("%s = %s" % (expr, str(value)))
200
201        if is_child:
202            Explorer.return_to_parent_value_prompt()
203            Explorer.return_to_parent_value()
204
205        return False
206
207    @staticmethod
208    def explore_type(name, datatype, is_child):
209        """Function to explore scalar types.
210        See Explorer.explore_type and Explorer.is_scalar_type for more
211        information.
212        """
213        if datatype.code == gdb.TYPE_CODE_ENUM:
214            if is_child:
215                print("%s is of an enumerated type '%s'." % (name, str(datatype)))
216            else:
217                print("'%s' is an enumerated type." % name)
218        else:
219            if is_child:
220                print("%s is of a scalar type '%s'." % (name, str(datatype)))
221            else:
222                print("'%s' is a scalar type." % name)
223
224        if is_child:
225            Explorer.return_to_enclosing_type_prompt()
226            Explorer.return_to_enclosing_type()
227
228        return False
229
230
231class PointerExplorer(object):
232    """Internal class used to explore pointer values."""
233
234    @staticmethod
235    def explore_expr(expr, value, is_child):
236        """Function to explore pointer values.
237        See Explorer.explore_expr for more information.
238        """
239        print(
240            "'%s' is a pointer to a value of type '%s'"
241            % (expr, str(value.type.target()))
242        )
243        option = input(
244            "Continue exploring it as a pointer to a single " "value [y/n]: "
245        )
246        if option == "y":
247            deref_value = None
248            try:
249                deref_value = value.dereference()
250                str(deref_value)
251            except gdb.MemoryError:
252                print(
253                    "'%s' a pointer pointing to an invalid memory " "location." % expr
254                )
255                if is_child:
256                    Explorer.return_to_parent_value_prompt()
257                return False
258            Explorer.explore_expr(
259                "*%s" % Explorer.guard_expr(expr), deref_value, is_child
260            )
261            return False
262
263        option = input("Continue exploring it as a pointer to an " "array [y/n]: ")
264        if option == "y":
265            while True:
266                index = 0
267                try:
268                    index = int(
269                        input(
270                            "Enter the index of the element you "
271                            "want to explore in '%s': " % expr
272                        )
273                    )
274                except ValueError:
275                    break
276                element_expr = "%s[%d]" % (Explorer.guard_expr(expr), index)
277                element = value[index]
278                try:
279                    str(element)
280                except gdb.MemoryError:
281                    print("Cannot read value at index %d." % index)
282                    continue
283                Explorer.explore_expr(element_expr, element, True)
284            return False
285
286        if is_child:
287            Explorer.return_to_parent_value()
288        return False
289
290    @staticmethod
291    def explore_type(name, datatype, is_child):
292        """Function to explore pointer types.
293        See Explorer.explore_type for more information.
294        """
295        target_type = datatype.target()
296        print("\n%s is a pointer to a value of type '%s'." % (name, str(target_type)))
297
298        Explorer.explore_type("the pointee type of %s" % name, target_type, is_child)
299        return False
300
301
302class ReferenceExplorer(object):
303    """Internal class used to explore reference (TYPE_CODE_REF) values."""
304
305    @staticmethod
306    def explore_expr(expr, value, is_child):
307        """Function to explore array values.
308        See Explorer.explore_expr for more information.
309        """
310        referenced_value = value.referenced_value()
311        Explorer.explore_expr(expr, referenced_value, is_child)
312        return False
313
314    @staticmethod
315    def explore_type(name, datatype, is_child):
316        """Function to explore pointer types.
317        See Explorer.explore_type for more information.
318        """
319        target_type = datatype.target()
320        Explorer.explore_type(name, target_type, is_child)
321        return False
322
323
324class ArrayExplorer(object):
325    """Internal class used to explore arrays."""
326
327    @staticmethod
328    def explore_expr(expr, value, is_child):
329        """Function to explore array values.
330        See Explorer.explore_expr for more information.
331        """
332        target_type = value.type.target()
333        print("'%s' is an array of '%s'." % (expr, str(target_type)))
334        index = 0
335        try:
336            index = int(
337                input(
338                    "Enter the index of the element you want to "
339                    "explore in '%s': " % expr
340                )
341            )
342        except ValueError:
343            if is_child:
344                Explorer.return_to_parent_value()
345            return False
346
347        element = None
348        try:
349            element = value[index]
350            str(element)
351        except gdb.MemoryError:
352            print("Cannot read value at index %d." % index)
353            input("Press enter to continue... ")
354            return True
355
356        Explorer.explore_expr(
357            "%s[%d]" % (Explorer.guard_expr(expr), index), element, True
358        )
359        return True
360
361    @staticmethod
362    def explore_type(name, datatype, is_child):
363        """Function to explore array types.
364        See Explorer.explore_type for more information.
365        """
366        target_type = datatype.target()
367        print("%s is an array of '%s'." % (name, str(target_type)))
368
369        Explorer.explore_type("the array element of %s" % name, target_type, is_child)
370        return False
371
372
373class CompoundExplorer(object):
374    """Internal class used to explore struct, classes and unions."""
375
376    @staticmethod
377    def _print_fields(print_list):
378        """Internal function which prints the fields of a struct/class/union."""
379        max_field_name_length = 0
380        for pair in print_list:
381            if max_field_name_length < len(pair[0]):
382                max_field_name_length = len(pair[0])
383
384        for pair in print_list:
385            print("  %*s = %s" % (max_field_name_length, pair[0], pair[1]))
386
387    @staticmethod
388    def _get_real_field_count(fields):
389        real_field_count = 0
390        for field in fields:
391            if not field.artificial:
392                real_field_count = real_field_count + 1
393
394        return real_field_count
395
396    @staticmethod
397    def explore_expr(expr, value, is_child):
398        """Function to explore structs/classes and union values.
399        See Explorer.explore_expr for more information.
400        """
401        datatype = value.type
402        type_code = datatype.code
403        fields = datatype.fields()
404
405        if type_code == gdb.TYPE_CODE_STRUCT:
406            type_desc = "struct/class"
407        else:
408            type_desc = "union"
409
410        if CompoundExplorer._get_real_field_count(fields) == 0:
411            print(
412                "The value of '%s' is a %s of type '%s' with no fields."
413                % (expr, type_desc, str(value.type))
414            )
415            if is_child:
416                Explorer.return_to_parent_value_prompt()
417            return False
418
419        print(
420            "The value of '%s' is a %s of type '%s' with the following "
421            "fields:\n" % (expr, type_desc, str(value.type))
422        )
423
424        has_explorable_fields = False
425        choice_to_compound_field_map = {}
426        current_choice = 0
427        print_list = []
428        for field in fields:
429            if field.artificial:
430                continue
431            field_full_name = Explorer.guard_expr(expr) + "." + field.name
432            if field.is_base_class:
433                field_value = value.cast(field.type)
434            else:
435                field_value = value[field.name]
436            literal_value = ""
437            if type_code == gdb.TYPE_CODE_UNION:
438                literal_value = "<Enter %d to explore this field of type " "'%s'>" % (
439                    current_choice,
440                    str(field.type),
441                )
442                has_explorable_fields = True
443            else:
444                if Explorer.is_scalar_type(field.type):
445                    literal_value = "%s .. (Value of type '%s')" % (
446                        str(field_value),
447                        str(field.type),
448                    )
449                else:
450                    if field.is_base_class:
451                        field_desc = "base class"
452                    else:
453                        field_desc = "field"
454                    literal_value = "<Enter %d to explore this %s of type " "'%s'>" % (
455                        current_choice,
456                        field_desc,
457                        str(field.type),
458                    )
459                    has_explorable_fields = True
460
461            choice_to_compound_field_map[str(current_choice)] = (
462                field_full_name,
463                field_value,
464            )
465            current_choice = current_choice + 1
466
467            print_list.append((field.name, literal_value))
468
469        CompoundExplorer._print_fields(print_list)
470        print("")
471
472        if has_explorable_fields:
473            choice = input("Enter the field number of choice: ")
474            if choice in choice_to_compound_field_map:
475                Explorer.explore_expr(
476                    choice_to_compound_field_map[choice][0],
477                    choice_to_compound_field_map[choice][1],
478                    True,
479                )
480                return True
481            else:
482                if is_child:
483                    Explorer.return_to_parent_value()
484        else:
485            if is_child:
486                Explorer.return_to_parent_value_prompt()
487
488        return False
489
490    @staticmethod
491    def explore_type(name, datatype, is_child):
492        """Function to explore struct/class and union types.
493        See Explorer.explore_type for more information.
494        """
495        type_code = datatype.code
496        type_desc = ""
497        if type_code == gdb.TYPE_CODE_STRUCT:
498            type_desc = "struct/class"
499        else:
500            type_desc = "union"
501
502        fields = datatype.fields()
503        if CompoundExplorer._get_real_field_count(fields) == 0:
504            if is_child:
505                print(
506                    "%s is a %s of type '%s' with no fields."
507                    % (name, type_desc, str(datatype))
508                )
509                Explorer.return_to_enclosing_type_prompt()
510            else:
511                print("'%s' is a %s with no fields." % (name, type_desc))
512            return False
513
514        if is_child:
515            print(
516                "%s is a %s of type '%s' "
517                "with the following fields:\n" % (name, type_desc, str(datatype))
518            )
519        else:
520            print("'%s' is a %s with the following " "fields:\n" % (name, type_desc))
521
522        current_choice = 0
523        choice_to_compound_field_map = {}
524        print_list = []
525        for field in fields:
526            if field.artificial:
527                continue
528            if field.is_base_class:
529                field_desc = "base class"
530            else:
531                field_desc = "field"
532            rhs = "<Enter %d to explore this %s of type '%s'>" % (
533                current_choice,
534                field_desc,
535                str(field.type),
536            )
537            print_list.append((field.name, rhs))
538            choice_to_compound_field_map[str(current_choice)] = (
539                field.name,
540                field.type,
541                field_desc,
542            )
543            current_choice = current_choice + 1
544
545        CompoundExplorer._print_fields(print_list)
546        print("")
547
548        if len(choice_to_compound_field_map) > 0:
549            choice = input("Enter the field number of choice: ")
550            if choice in choice_to_compound_field_map:
551                if is_child:
552                    new_name = "%s '%s' of %s" % (
553                        choice_to_compound_field_map[choice][2],
554                        choice_to_compound_field_map[choice][0],
555                        name,
556                    )
557                else:
558                    new_name = "%s '%s' of '%s'" % (
559                        choice_to_compound_field_map[choice][2],
560                        choice_to_compound_field_map[choice][0],
561                        name,
562                    )
563                Explorer.explore_type(
564                    new_name, choice_to_compound_field_map[choice][1], True
565                )
566                return True
567            else:
568                if is_child:
569                    Explorer.return_to_enclosing_type()
570        else:
571            if is_child:
572                Explorer.return_to_enclosing_type_prompt()
573
574        return False
575
576
577class TypedefExplorer(object):
578    """Internal class used to explore values whose type is a typedef."""
579
580    @staticmethod
581    def explore_expr(expr, value, is_child):
582        """Function to explore typedef values.
583        See Explorer.explore_expr for more information.
584        """
585        actual_type = value.type.strip_typedefs()
586        print(
587            "The value of '%s' is of type '%s' "
588            "which is a typedef of type '%s'"
589            % (expr, str(value.type), str(actual_type))
590        )
591
592        Explorer.explore_expr(expr, value.cast(actual_type), is_child)
593        return False
594
595    @staticmethod
596    def explore_type(name, datatype, is_child):
597        """Function to explore typedef types.
598        See Explorer.explore_type for more information.
599        """
600        actual_type = datatype.strip_typedefs()
601        if is_child:
602            print(
603                "The type of %s is a typedef of type '%s'." % (name, str(actual_type))
604            )
605        else:
606            print("The type '%s' is a typedef of type '%s'." % (name, str(actual_type)))
607
608        Explorer.explore_type(name, actual_type, is_child)
609        return False
610
611
612class ExploreUtils(object):
613    """Internal class which provides utilities for the main command classes."""
614
615    @staticmethod
616    def check_args(name, arg_str):
617        """Utility to check if adequate number of arguments are passed to an
618        explore command.
619
620        Arguments:
621            name: The name of the explore command.
622            arg_str: The argument string passed to the explore command.
623
624        Returns:
625            True if adequate arguments are passed, false otherwise.
626
627        Raises:
628            gdb.GdbError if adequate arguments are not passed.
629        """
630        if len(arg_str) < 1:
631            raise gdb.GdbError("ERROR: '%s' requires an argument." % name)
632            return False
633        else:
634            return True
635
636    @staticmethod
637    def get_type_from_str(type_str):
638        """A utility function to deduce the gdb.Type value from a string
639        representing the type.
640
641        Arguments:
642            type_str: The type string from which the gdb.Type value should be
643                      deduced.
644
645        Returns:
646            The deduced gdb.Type value if possible, None otherwise.
647        """
648        try:
649            # Assume the current language to be C/C++ and make a try.
650            return gdb.parse_and_eval("(%s *)0" % type_str).type.target()
651        except RuntimeError:
652            # If assumption of current language to be C/C++ was wrong, then
653            # lookup the type using the API.
654            try:
655                return gdb.lookup_type(type_str)
656            except RuntimeError:
657                return None
658
659    @staticmethod
660    def get_value_from_str(value_str):
661        """A utility function to deduce the gdb.Value value from a string
662        representing the value.
663
664        Arguments:
665            value_str: The value string from which the gdb.Value value should
666                       be deduced.
667
668        Returns:
669            The deduced gdb.Value value if possible, None otherwise.
670        """
671        try:
672            return gdb.parse_and_eval(value_str)
673        except RuntimeError:
674            return None
675
676
677class ExploreCommand(gdb.Command):
678    """Explore a value or a type valid in the current context.
679
680    Usage: explore ARG
681
682    - ARG is either a valid expression or a type name.
683    - At any stage of exploration, hit the return key (instead of a
684    choice, if any) to return to the enclosing type or value."""
685
686    def __init__(self):
687        super(ExploreCommand, self).__init__(
688            name="explore", command_class=gdb.COMMAND_DATA, prefix=True
689        )
690
691    def invoke(self, arg_str, from_tty):
692        if ExploreUtils.check_args("explore", arg_str) is False:
693            return
694
695        # Check if it is a value
696        value = ExploreUtils.get_value_from_str(arg_str)
697        if value is not None:
698            Explorer.explore_expr(arg_str, value, False)
699            return
700
701        # If it is not a value, check if it is a type
702        datatype = ExploreUtils.get_type_from_str(arg_str)
703        if datatype is not None:
704            Explorer.explore_type(arg_str, datatype, False)
705            return
706
707        # If it is neither a value nor a type, raise an error.
708        raise gdb.GdbError(
709            (
710                "'%s' neither evaluates to a value nor is a type "
711                "in the current context." % arg_str
712            )
713        )
714
715
716class ExploreValueCommand(gdb.Command):
717    """Explore value of an expression valid in the current context.
718
719    Usage: explore value ARG
720
721    - ARG is a valid expression.
722    - At any stage of exploration, hit the return key (instead of a
723    choice, if any) to return to the enclosing value."""
724
725    def __init__(self):
726        super(ExploreValueCommand, self).__init__(
727            name="explore value", command_class=gdb.COMMAND_DATA
728        )
729
730    def invoke(self, arg_str, from_tty):
731        if ExploreUtils.check_args("explore value", arg_str) is False:
732            return
733
734        value = ExploreUtils.get_value_from_str(arg_str)
735        if value is None:
736            raise gdb.GdbError(
737                (
738                    " '%s' does not evaluate to a value in the current "
739                    "context." % arg_str
740                )
741            )
742            return
743
744        Explorer.explore_expr(arg_str, value, False)
745
746
747class ExploreTypeCommand(gdb.Command):
748    """Explore a type or the type of an expression.
749
750    Usage: explore type ARG
751
752    - ARG is a valid expression or a type name.
753    - At any stage of exploration, hit the return key (instead of a
754    choice, if any) to return to the enclosing type."""
755
756    def __init__(self):
757        super(ExploreTypeCommand, self).__init__(
758            name="explore type", command_class=gdb.COMMAND_DATA
759        )
760
761    def invoke(self, arg_str, from_tty):
762        if ExploreUtils.check_args("explore type", arg_str) is False:
763            return
764
765        datatype = ExploreUtils.get_type_from_str(arg_str)
766        if datatype is not None:
767            Explorer.explore_type(arg_str, datatype, False)
768            return
769
770        value = ExploreUtils.get_value_from_str(arg_str)
771        if value is not None:
772            print("'%s' is of type '%s'." % (arg_str, str(value.type)))
773            Explorer.explore_type(str(value.type), value.type, False)
774            return
775
776        raise gdb.GdbError(
777            ("'%s' is not a type or value in the current " "context." % arg_str)
778        )
779
780
781Explorer.init_env()
782
783ExploreCommand()
784ExploreValueCommand()
785ExploreTypeCommand()
786