Path: blob/master/misc/utility/godot_gdb_pretty_print.py
9896 views
"""1Load this file to your GDB session to enable pretty-printing of some Godot C++ types.23GDB command: `source misc/utility/godot_gdb_pretty_print.py`.45To load these automatically in Visual Studio Code, add the source command to6the `setupCommands` of your configuration in `launch.json`:7```json8"setupCommands": [9...10{11"description": "Load custom pretty-printers for Godot types.",12"text": "source ${workspaceFolder}/misc/utility/godot_gdb_pretty_print.py"13}14]15```16Other UIs that use GDB under the hood are likely to have their own ways to achieve this.1718To debug this script it's easiest to use the interactive python from a command-line19GDB session. Stop at a breakpoint, then use python-interactive to enter the python shell20and acquire a `Value` object using `gdb.selected_frame().read_var("variable name")`.21From there you can figure out how to print it nicely.22"""2324import re2526import gdb # type: ignore27import gdb.printing # type: ignore282930# Printer for Godot StringName variables.31class GodotStringNamePrinter:32def __init__(self, value):33self.value = value3435def to_string(self):36return self.value["_data"]["name"]["_cowdata"]["_ptr"]3738# Hint that the object is string-like.39def display_hint(self):40return "string"414243# Printer for Godot String variables.44class GodotStringPrinter:45def __init__(self, value):46self.value = value4748def to_string(self):49return self.value["_cowdata"]["_ptr"]5051# Hint that the object is string-like.52def display_hint(self):53return "string"545556# Printer for Godot Vector variables.57class GodotVectorPrinter:58def __init__(self, value):59self.value = value6061# The COW (Copy On Write) object does a bunch of pointer arithmetic to access62# its members.63# The offsets are constants on the C++ side, optimized out, so not accessible to us.64# I'll just hard code the observed values and hope they are the same forever.65# See core/templates/cowdata.h66SIZE_OFFSET = 867DATA_OFFSET = 166869# Figures out the number of elements in the vector.70def get_size(self):71cowdata = self.value["_cowdata"]72if cowdata["_ptr"] == 0:73return 074else:75# The ptr member of cowdata does not point to the beginning of the76# cowdata. It points to the beginning of the data section of the cowdata.77# To get to the length section, we must back up to the beginning of the struct,78# then move back forward to the size.79# cf. CowData::_get_size80ptr = cowdata["_ptr"].cast(gdb.lookup_type("uint8_t").pointer())81return int((ptr - self.DATA_OFFSET + self.SIZE_OFFSET).dereference())8283# Lists children of the value, in this case the vector's items.84def children(self):85# Return nothing if ptr is null.86ptr = self.value["_cowdata"]["_ptr"]87if ptr == 0:88return89# Yield the items one by one.90for i in range(self.get_size()):91yield str(i), (ptr + i).dereference()9293def to_string(self):94return "%s [%d]" % (self.value.type.name, self.get_size())9596# Hint that the object is array-like.97def display_hint(self):98return "array"99100101VECTOR_REGEX = re.compile("^Vector<.*$")102103104# Tries to find a pretty printer for a debugger value.105def lookup_pretty_printer(value):106if value.type.name == "StringName":107return GodotStringNamePrinter(value)108if value.type.name == "String":109return GodotStringPrinter(value)110if value.type.name and VECTOR_REGEX.match(value.type.name):111return GodotVectorPrinter(value)112return None113114115# Register our printer lookup function.116# The first parameter could be used to limit the scope of the printer117# to a specific object file, but that is unnecessary for us.118gdb.printing.register_pretty_printer(None, lookup_pretty_printer)119120121