-
-
Notifications
You must be signed in to change notification settings - Fork 21.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a GDB pretty printer to aid in debugging #91280
Merged
akien-mga
merged 1 commit into
godotengine:master
from
Mitten-O:topic/gdb-pretty-printer
May 11, 2024
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
#!/usr/bin/env python3 | ||
# Load this file to your GDB session to enable pretty-printing | ||
# of some Godot C++ types. | ||
# GDB command: source misc/scripts/godot_gdb_pretty_print.py | ||
# | ||
# To load these automatically in Visual Studio Code, | ||
# add the source command to the setupCommands of your configuration | ||
# in launch.json. | ||
# "setupCommands": [ | ||
# ... | ||
# { | ||
# "description": "Load custom pretty-printers for Godot types.", | ||
# "text": "source ${workspaceRoot}/misc/scripts/godot_gdb_pretty_print.py" | ||
# } | ||
# ] | ||
# Other UI:s that use GDB under the hood are likely to have their own ways to achieve this. | ||
# | ||
# To debug this script it's easiest to use the interactive python from a command-line | ||
# GDB session. Stop at a breakpoint, then use | ||
# python-interactive to enter the python shell and | ||
# acquire a Value object using gdb.selected_frame().read_var("variable name"). | ||
# From there you can figure out how to print it nicely. | ||
import re | ||
|
||
import gdb | ||
|
||
|
||
# Printer for Godot StringName variables. | ||
class GodotStringNamePrinter: | ||
def __init__(self, value): | ||
self.value = value | ||
|
||
def to_string(self): | ||
return self.value["_data"]["name"]["_cowdata"]["_ptr"] | ||
|
||
# Hint that the object is string-like. | ||
def display_hint(self): | ||
return "string" | ||
|
||
|
||
# Printer for Godot String variables. | ||
class GodotStringPrinter: | ||
def __init__(self, value): | ||
self.value = value | ||
|
||
def to_string(self): | ||
return self.value["_cowdata"]["_ptr"] | ||
|
||
# Hint that the object is string-like. | ||
def display_hint(self): | ||
return "string" | ||
|
||
|
||
# Printer for Godot Vector variables. | ||
class GodotVectorPrinter: | ||
def __init__(self, value): | ||
self.value = value | ||
|
||
# The COW (Copy On Write) object does a bunch of pointer arithmetic to access | ||
# its members. | ||
# The offsets are constants on the C++ side, optimized out, so not accessible to us. | ||
# I'll just hard code the observed values and hope they are the same forever. | ||
# See core/templates/cowdata.h | ||
SIZE_OFFSET = 8 | ||
DATA_OFFSET = 16 | ||
|
||
# Figures out the number of elements in the vector. | ||
def get_size(self): | ||
cowdata = self.value["_cowdata"] | ||
if cowdata["_ptr"] == 0: | ||
return 0 | ||
else: | ||
# The ptr member of cowdata does not point to the beginning of the | ||
# cowdata. It points to the beginning of the data section of the cowdata. | ||
# To get to the length section, we must back up to the beginning of the struct, | ||
# then move back forward to the size. | ||
# cf. CowData::_get_size | ||
ptr = cowdata["_ptr"].cast(gdb.lookup_type("uint8_t").pointer()) | ||
return int((ptr - self.DATA_OFFSET + self.SIZE_OFFSET).dereference()) | ||
|
||
# Lists children of the value, in this case the vector's items. | ||
def children(self): | ||
# Return nothing if ptr is null. | ||
ptr = self.value["_cowdata"]["_ptr"] | ||
if ptr == 0: | ||
return | ||
# Yield the items one by one. | ||
for i in range(self.get_size()): | ||
yield str(i), (ptr + i).dereference() | ||
|
||
def to_string(self): | ||
return "%s [%d]" % (self.value.type.name, self.get_size()) | ||
|
||
# Hint that the object is array-like. | ||
def display_hint(self): | ||
return "array" | ||
|
||
|
||
VECTOR_REGEX = re.compile("^Vector<.*$") | ||
|
||
|
||
# Tries to find a pretty printer for a debugger value. | ||
def lookup_pretty_printer(value): | ||
if value.type.name == "StringName": | ||
return GodotStringNamePrinter(value) | ||
if value.type.name == "String": | ||
return GodotStringPrinter(value) | ||
if value.type.name and VECTOR_REGEX.match(value.type.name): | ||
return GodotVectorPrinter(value) | ||
return None | ||
|
||
|
||
# Register our printer lookup function. | ||
# The first parameter could be used to limit the scope of the printer | ||
# to a specific object file, but that is unnecessary for us. | ||
gdb.printing.register_pretty_printer(None, lookup_pretty_printer) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add shebang (and make file executable with
chmod +x
):Also, remember to format the file with
black -l120 path/to/file.py
(it's what we use for other scripts in the repository). I also suggest runningisort path/to/file.py
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.