godot_gdb_pretty_print.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. #!/usr/bin/env python3
  2. # Load this file to your GDB session to enable pretty-printing
  3. # of some Godot C++ types.
  4. # GDB command: source misc/scripts/godot_gdb_pretty_print.py
  5. #
  6. # To load these automatically in Visual Studio Code,
  7. # add the source command to the setupCommands of your configuration
  8. # in launch.json.
  9. # "setupCommands": [
  10. # ...
  11. # {
  12. # "description": "Load custom pretty-printers for Godot types.",
  13. # "text": "source ${workspaceRoot}/misc/scripts/godot_gdb_pretty_print.py"
  14. # }
  15. # ]
  16. # Other UI:s that use GDB under the hood are likely to have their own ways to achieve this.
  17. #
  18. # To debug this script it's easiest to use the interactive python from a command-line
  19. # GDB session. Stop at a breakpoint, then use
  20. # python-interactive to enter the python shell and
  21. # acquire a Value object using gdb.selected_frame().read_var("variable name").
  22. # From there you can figure out how to print it nicely.
  23. import re
  24. import gdb
  25. # Printer for Godot StringName variables.
  26. class GodotStringNamePrinter:
  27. def __init__(self, value):
  28. self.value = value
  29. def to_string(self):
  30. return self.value["_data"]["name"]["_cowdata"]["_ptr"]
  31. # Hint that the object is string-like.
  32. def display_hint(self):
  33. return "string"
  34. # Printer for Godot String variables.
  35. class GodotStringPrinter:
  36. def __init__(self, value):
  37. self.value = value
  38. def to_string(self):
  39. return self.value["_cowdata"]["_ptr"]
  40. # Hint that the object is string-like.
  41. def display_hint(self):
  42. return "string"
  43. # Printer for Godot Vector variables.
  44. class GodotVectorPrinter:
  45. def __init__(self, value):
  46. self.value = value
  47. # The COW (Copy On Write) object does a bunch of pointer arithmetic to access
  48. # its members.
  49. # The offsets are constants on the C++ side, optimized out, so not accessible to us.
  50. # I'll just hard code the observed values and hope they are the same forever.
  51. # See core/templates/cowdata.h
  52. SIZE_OFFSET = 8
  53. DATA_OFFSET = 16
  54. # Figures out the number of elements in the vector.
  55. def get_size(self):
  56. cowdata = self.value["_cowdata"]
  57. if cowdata["_ptr"] == 0:
  58. return 0
  59. else:
  60. # The ptr member of cowdata does not point to the beginning of the
  61. # cowdata. It points to the beginning of the data section of the cowdata.
  62. # To get to the length section, we must back up to the beginning of the struct,
  63. # then move back forward to the size.
  64. # cf. CowData::_get_size
  65. ptr = cowdata["_ptr"].cast(gdb.lookup_type("uint8_t").pointer())
  66. return int((ptr - self.DATA_OFFSET + self.SIZE_OFFSET).dereference())
  67. # Lists children of the value, in this case the vector's items.
  68. def children(self):
  69. # Return nothing if ptr is null.
  70. ptr = self.value["_cowdata"]["_ptr"]
  71. if ptr == 0:
  72. return
  73. # Yield the items one by one.
  74. for i in range(self.get_size()):
  75. yield str(i), (ptr + i).dereference()
  76. def to_string(self):
  77. return "%s [%d]" % (self.value.type.name, self.get_size())
  78. # Hint that the object is array-like.
  79. def display_hint(self):
  80. return "array"
  81. VECTOR_REGEX = re.compile("^Vector<.*$")
  82. # Tries to find a pretty printer for a debugger value.
  83. def lookup_pretty_printer(value):
  84. if value.type.name == "StringName":
  85. return GodotStringNamePrinter(value)
  86. if value.type.name == "String":
  87. return GodotStringPrinter(value)
  88. if value.type.name and VECTOR_REGEX.match(value.type.name):
  89. return GodotVectorPrinter(value)
  90. return None
  91. # Register our printer lookup function.
  92. # The first parameter could be used to limit the scope of the printer
  93. # to a specific object file, but that is unnecessary for us.
  94. gdb.printing.register_pretty_printer(None, lookup_pretty_printer)