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