Add Chromium-only Blender WebEngine parity work
This commit is contained in:
684
blender-5.2.0/tools/debug/gdb/blender_gdb_extension.py
Normal file
684
blender-5.2.0/tools/debug/gdb/blender_gdb_extension.py
Normal file
@@ -0,0 +1,684 @@
|
||||
# SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
'''
|
||||
This file can be loaded into GDB to improve the debugging experience.
|
||||
|
||||
It has the following features:
|
||||
* Pretty printers for various types like `blender::Map` and `blender::IndexMask`.
|
||||
* Frame filters to reduce the complexity of backtraces.
|
||||
|
||||
The basic setup is simple. Add the following line to a `~/.gdbinit` file.
|
||||
Everything in this file is run by GDB when it is started.
|
||||
```
|
||||
source ~/blender-git/blender/tools/debug/gdb/blender_gdb_extension.py
|
||||
```
|
||||
|
||||
To validate that things are registered correctly:
|
||||
1. Start `gdb`.
|
||||
2. Run `info pretty-printer` and check for `blender-pretty-printers`.
|
||||
3. Run `info frame-filter` and check for `blender-frame-filters`.
|
||||
'''
|
||||
|
||||
__all__ = (
|
||||
# Not used externally but functions as a `main`.
|
||||
"register",
|
||||
)
|
||||
|
||||
import gdb
|
||||
import gdb.printing
|
||||
from contextlib import contextmanager
|
||||
from gdb.FrameDecorator import FrameDecorator
|
||||
|
||||
|
||||
@contextmanager
|
||||
def eval_var(variable_name, value):
|
||||
'''
|
||||
Creates a context in which the given variable name has the given value.
|
||||
'''
|
||||
gdb.set_convenience_variable(variable_name, value)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
gdb.set_convenience_variable(variable_name, None)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def ensure_unwind_on_signal():
|
||||
'''
|
||||
Creates a context in which gdb can attempt evaluating functions with
|
||||
less risk of crashing the program. If there is an error (e.g. a segfault)
|
||||
gdb will unwind the stack, raise a Python exception, but otherwise allows
|
||||
continuing the debugging session.
|
||||
'''
|
||||
was_on = "is on." in gdb.execute("show unwindonsignal", to_string=True)
|
||||
if not was_on:
|
||||
gdb.execute("set unwindonsignal on")
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
if not was_on:
|
||||
gdb.execute("set unwindonsignal off")
|
||||
|
||||
|
||||
class VectorPrinter:
|
||||
def __init__(self, value: gdb.Value):
|
||||
self.value = value
|
||||
|
||||
def to_string(self):
|
||||
vec_begin = self.value["begin_"]
|
||||
vec_end = self.value["end_"]
|
||||
vec_capacity_end = self.value["end_"]
|
||||
vec_size = vec_end - vec_begin
|
||||
vec_capacity = vec_capacity_end - vec_begin
|
||||
return f"Size: {vec_size}"
|
||||
|
||||
def children(self):
|
||||
begin = self.value["begin_"]
|
||||
end = self.value["end_"]
|
||||
size = end - begin
|
||||
for i in range(size):
|
||||
yield str(i), begin[i]
|
||||
|
||||
def display_hint(self):
|
||||
return "array"
|
||||
|
||||
|
||||
class SetPrinter:
|
||||
def __init__(self, value: gdb.Value):
|
||||
self.value = value
|
||||
self.key_type = value.type.template_argument(0)
|
||||
|
||||
def to_string(self):
|
||||
size = int(
|
||||
self.value["occupied_and_removed_slots_"] - self.value["removed_slots_"]
|
||||
)
|
||||
return f"Size: {size}"
|
||||
|
||||
def children(self):
|
||||
slots = self.value["slots_"]["data_"]
|
||||
slots_num = int(self.value["slots_"]["size_"])
|
||||
for i in range(slots_num):
|
||||
slot = slots[i]
|
||||
if self.key_type.code == gdb.TYPE_CODE_PTR:
|
||||
key = slot["key_"]
|
||||
key_int = int(key)
|
||||
# The key has two special values for an empty and removed slot.
|
||||
is_occupied = key_int < 2**64 - 2
|
||||
if is_occupied:
|
||||
yield str(i), key
|
||||
else:
|
||||
slot_state = int(slot["state_"])
|
||||
is_occupied = slot_state == 1
|
||||
if is_occupied:
|
||||
key = slot["key_buffer_"].cast(self.key_type)
|
||||
yield str(i), key
|
||||
|
||||
def display_hint(self):
|
||||
return "array"
|
||||
|
||||
|
||||
class MapPrinter:
|
||||
def __init__(self, value: gdb.Value):
|
||||
self.value = value
|
||||
self.key_type = value.type.template_argument(0)
|
||||
self.value_type = value.type.template_argument(1)
|
||||
|
||||
def to_string(self):
|
||||
size = int(
|
||||
self.value["occupied_and_removed_slots_"] - self.value["removed_slots_"]
|
||||
)
|
||||
return f"Size: {size}"
|
||||
|
||||
def children(self):
|
||||
slots = self.value["slots_"]["data_"]
|
||||
slots_num = int(self.value["slots_"]["size_"])
|
||||
for i in range(slots_num):
|
||||
slot = slots[i]
|
||||
if self.key_type.code == gdb.TYPE_CODE_PTR:
|
||||
key = slot["key_"]
|
||||
key_int = int(key)
|
||||
# The key has two special values for an empty and removed slot.
|
||||
is_occupied = key_int < 2**64 - 2
|
||||
if is_occupied:
|
||||
value = slot["value_buffer_"].cast(self.value_type)
|
||||
yield "Key", key
|
||||
yield "Value", value
|
||||
else:
|
||||
slot_state = int(slot["state_"])
|
||||
is_occupied = slot_state == 1
|
||||
if is_occupied:
|
||||
key = slot["key_buffer_"].cast(self.key_type)
|
||||
value = slot["value_buffer_"].cast(self.value_type)
|
||||
yield "Key", key
|
||||
yield "Value", value
|
||||
|
||||
def display_hint(self):
|
||||
return "map"
|
||||
|
||||
|
||||
class MultiValueMapPrinter:
|
||||
def __init__(self, value: gdb.Value):
|
||||
self.value = value
|
||||
self.map_value = value["map_"]
|
||||
|
||||
def to_string(self):
|
||||
return MapPrinter(self.map_value).to_string()
|
||||
|
||||
def children(self):
|
||||
return MapPrinter(self.map_value).children()
|
||||
|
||||
def display_hint(self):
|
||||
return MapPrinter(self.map_value).display_hint()
|
||||
|
||||
|
||||
class TypedBufferPrinter:
|
||||
def __init__(self, value: gdb.Value):
|
||||
self.value = value
|
||||
self.type = value.type.template_argument(0)
|
||||
self.size = value.type.template_argument(1)
|
||||
|
||||
def children(self):
|
||||
data = self.value.cast(self.type).address
|
||||
for i in range(self.size):
|
||||
yield str(i), data[i]
|
||||
|
||||
def display_hint(self):
|
||||
return "array"
|
||||
|
||||
|
||||
class ArrayPrinter:
|
||||
def __init__(self, value: gdb.Value):
|
||||
self.value = value
|
||||
|
||||
def to_string(self):
|
||||
size = self.value["size_"]
|
||||
return f"Size: {size}"
|
||||
|
||||
def children(self):
|
||||
data = self.value["data_"]
|
||||
size = self.value["size_"]
|
||||
for i in range(size):
|
||||
yield str(i), data[i]
|
||||
|
||||
def display_hint(self):
|
||||
return "array"
|
||||
|
||||
|
||||
class VectorSetPrinter:
|
||||
def __init__(self, value: gdb.Value):
|
||||
self.value = value
|
||||
|
||||
def get_size(self):
|
||||
return int(
|
||||
self.value["occupied_and_removed_slots_"] - self.value["removed_slots_"]
|
||||
)
|
||||
|
||||
def to_string(self):
|
||||
size = self.get_size()
|
||||
return f"Size: {size}"
|
||||
|
||||
def children(self):
|
||||
data = self.value["keys_"]
|
||||
size = self.get_size()
|
||||
for i in range(size):
|
||||
yield str(i), data[i]
|
||||
|
||||
def display_hint(self):
|
||||
return "array"
|
||||
|
||||
|
||||
class VArrayPrinter:
|
||||
def __init__(self, value: gdb.Value):
|
||||
self.value = value
|
||||
|
||||
def get_size(self):
|
||||
impl = self.value["impl_"]
|
||||
size = int(impl["size_"])
|
||||
return size
|
||||
|
||||
def to_string(self):
|
||||
size = self.get_size()
|
||||
with eval_var("varray", self.value.address):
|
||||
is_single = gdb.parse_and_eval("$varray->is_single()")
|
||||
if is_single and size >= 1:
|
||||
with eval_var("varray", self.value.address):
|
||||
single_value = gdb.parse_and_eval("$varray->get_internal_single()")
|
||||
return f"Size: {size}, Single Value: {single_value}"
|
||||
return f"Size: {size}"
|
||||
|
||||
def children(self):
|
||||
size = self.get_size()
|
||||
impl = self.value["impl_"]
|
||||
for i in range(size):
|
||||
with eval_var("varray_impl", impl):
|
||||
value_at_index = gdb.parse_and_eval(f"$varray_impl->get({i})")
|
||||
yield str(i), value_at_index
|
||||
|
||||
def display_hint(self):
|
||||
return "array"
|
||||
|
||||
|
||||
class MathVectorPrinter:
|
||||
def __init__(self, value: gdb.Value):
|
||||
self.value = value
|
||||
self.base_type = value.type.template_argument(0)
|
||||
self.size = value.type.template_argument(1)
|
||||
|
||||
def to_string(self):
|
||||
values = [str(self.get(i)) for i in range(self.size)]
|
||||
return "(" + ", ".join(values) + ")"
|
||||
|
||||
def children(self):
|
||||
for i in range(self.size):
|
||||
yield str(i), self.get(i)
|
||||
|
||||
def get(self, i):
|
||||
# Avoid taking pointer of value in case the pointer is not available.
|
||||
if 2 <= self.size <= 4:
|
||||
if i == 0:
|
||||
return self.value["x"]
|
||||
if i == 1:
|
||||
return self.value["y"]
|
||||
if i == 2:
|
||||
return self.value["z"]
|
||||
if i == 3:
|
||||
return self.value["w"]
|
||||
return self.value["values"][i]
|
||||
|
||||
def display_hint(self):
|
||||
return "array"
|
||||
|
||||
|
||||
class SpanPrinter:
|
||||
def __init__(self, value: gdb.Value):
|
||||
self.value = value
|
||||
|
||||
def to_string(self):
|
||||
size = self.value["size_"]
|
||||
return f"Size: {size}"
|
||||
|
||||
def children(self):
|
||||
data = self.value["data_"]
|
||||
size = self.value["size_"]
|
||||
for i in range(size):
|
||||
yield str(i), data[i]
|
||||
|
||||
def display_hint(self):
|
||||
return "array"
|
||||
|
||||
|
||||
class StringRefPrinter:
|
||||
def __init__(self, value: gdb.Value):
|
||||
self.value = value
|
||||
|
||||
def to_string(self):
|
||||
data = self.value["data_"]
|
||||
size = int(self.value["size_"])
|
||||
if size == 0:
|
||||
return ""
|
||||
return data.string(length=size)
|
||||
|
||||
def display_hint(self):
|
||||
return "string"
|
||||
|
||||
|
||||
class IndexRangePrinter:
|
||||
def __init__(self, value: gdb.Value):
|
||||
self.value = value
|
||||
|
||||
def to_string(self):
|
||||
start = int(self.value["start_"])
|
||||
size = int(self.value["size_"])
|
||||
if size == 0:
|
||||
return "Size: 0"
|
||||
return f"Size: {size}, [{start} - {start + size - 1}]"
|
||||
|
||||
def display_hint(self):
|
||||
return None
|
||||
|
||||
|
||||
class IndexMaskPrinter:
|
||||
def __init__(self, value: gdb.Value):
|
||||
self.value = value
|
||||
|
||||
def to_string(self):
|
||||
size = int(self.value["indices_num_"])
|
||||
if size == 0:
|
||||
return "Size: 0"
|
||||
|
||||
segments_num = int(self.value["segments_num_"])
|
||||
|
||||
with eval_var("mask", self.value.address):
|
||||
first = int(gdb.parse_and_eval("$mask->first()"))
|
||||
last = int(gdb.parse_and_eval("$mask->last()"))
|
||||
|
||||
is_range = last - first + 1 == size
|
||||
if is_range:
|
||||
return f"Size: {size}, [{first} - {last}], Segments: {segments_num}"
|
||||
return f"Size: {size}, Segments: {segments_num}"
|
||||
|
||||
def children(self):
|
||||
segments_num = int(self.value["segments_num_"])
|
||||
prev_cumulative_size = int(self.value["cumulative_segment_sizes_"][0])
|
||||
for segment_i in range(segments_num):
|
||||
cumulative_size = int(
|
||||
self.value["cumulative_segment_sizes_"][segment_i + 1]
|
||||
)
|
||||
full_segment_size = cumulative_size - prev_cumulative_size
|
||||
indices_ptr = self.value["indices_by_segment_"][segment_i]
|
||||
offset = self.value["segment_offsets_"][segment_i]
|
||||
with eval_var("mask", self.value.address):
|
||||
segment = gdb.parse_and_eval(f"$mask->segment({segment_i})")
|
||||
yield str(segment_i), segment
|
||||
prev_cumulative_size = cumulative_size
|
||||
|
||||
def display_hint(self):
|
||||
return "array"
|
||||
|
||||
|
||||
class IndexMaskSegmentPrinter:
|
||||
def __init__(self, value: gdb.Value):
|
||||
self.value = value
|
||||
|
||||
def to_string(self):
|
||||
size = int(self.value["data_"]["size_"])
|
||||
if size == 0:
|
||||
return "Size: 0"
|
||||
|
||||
offset = int(self.value["offset_"])
|
||||
first = int(self.value["data_"]["data_"][0]) + offset
|
||||
last = int(self.value["data_"]["data_"][size - 1]) + offset
|
||||
is_range = last - first + 1 == size
|
||||
if is_range:
|
||||
return f"Size: {size}, [{first} - {last}]"
|
||||
|
||||
return f"Size: {size}"
|
||||
|
||||
def children(self):
|
||||
size = int(self.value["data_"]["size_"])
|
||||
offset = int(self.value["offset_"])
|
||||
for i in range(size):
|
||||
element = int(self.value["data_"]["data_"][i])
|
||||
index = element + offset
|
||||
yield str(i), index
|
||||
|
||||
def display_hint(self):
|
||||
return "array"
|
||||
|
||||
|
||||
class OffsetIndicesPrinter:
|
||||
def __init__(self, value: gdb.Value):
|
||||
self.value = value
|
||||
|
||||
def to_string(self):
|
||||
with eval_var("indices", self.value.address):
|
||||
size = gdb.parse_and_eval("$indices->size()")
|
||||
return f"Size: {size}"
|
||||
|
||||
def children(self):
|
||||
with eval_var("indices", self.value.address):
|
||||
size = gdb.parse_and_eval("$indices->size()")
|
||||
for i in range(size):
|
||||
with eval_var("indices", self.value.address):
|
||||
element = gdb.parse_and_eval(f"(*$indices)[{i}]")
|
||||
yield str(i), element
|
||||
|
||||
def display_hint(self):
|
||||
return "array"
|
||||
|
||||
|
||||
class ErrorHandlingPrinter:
|
||||
'''
|
||||
This class can wrap another printer and adds additional error handling on top.
|
||||
'''
|
||||
|
||||
def __init__(self, printer):
|
||||
self.printer = printer
|
||||
|
||||
def to_string(self):
|
||||
try:
|
||||
with ensure_unwind_on_signal():
|
||||
return self.printer.to_string()
|
||||
except Exception as ex:
|
||||
return f"Error: {ex!s}"
|
||||
|
||||
def children(self):
|
||||
try:
|
||||
children_generator = self.printer.children()
|
||||
while True:
|
||||
with ensure_unwind_on_signal():
|
||||
next_child = next(children_generator)
|
||||
yield next_child
|
||||
except:
|
||||
return
|
||||
|
||||
def display_hint(self):
|
||||
return self.printer.display_hint()
|
||||
|
||||
|
||||
class BlenderPrettyPrinters(gdb.printing.PrettyPrinter):
|
||||
def __init__(self):
|
||||
super().__init__("blender-pretty-printers")
|
||||
|
||||
def get_pretty_printer(self, value: gdb.Value):
|
||||
value_type = value.type
|
||||
if value_type is None:
|
||||
return None
|
||||
if value_type.code == gdb.TYPE_CODE_PTR:
|
||||
return None
|
||||
type_name = value_type.strip_typedefs().name
|
||||
if type_name is None:
|
||||
return None
|
||||
if type_name.startswith("blender::Vector<"):
|
||||
return VectorPrinter(value)
|
||||
if type_name.startswith("blender::Set<"):
|
||||
return SetPrinter(value)
|
||||
if type_name.startswith("blender::Map<"):
|
||||
return MapPrinter(value)
|
||||
if type_name.startswith("blender::MultiValueMap<"):
|
||||
return MultiValueMapPrinter(value)
|
||||
if type_name.startswith("blender::TypedBuffer<"):
|
||||
return TypedBufferPrinter(value)
|
||||
if type_name.startswith("blender::Array<"):
|
||||
return ArrayPrinter(value)
|
||||
if type_name.startswith("blender::VectorSet<"):
|
||||
return VectorSetPrinter(value)
|
||||
if type_name.startswith("blender::VArray<"):
|
||||
return VArrayPrinter(value)
|
||||
if type_name.startswith("blender::VMutableArray<"):
|
||||
return VArrayPrinter(value)
|
||||
if type_name.startswith("blender::VecBase<"):
|
||||
return MathVectorPrinter(value)
|
||||
if type_name.startswith("blender::Span<"):
|
||||
return SpanPrinter(value)
|
||||
if type_name.startswith("blender::MutableSpan<"):
|
||||
return SpanPrinter(value)
|
||||
if type_name.startswith("blender::VArraySpan<"):
|
||||
return SpanPrinter(value)
|
||||
if type_name == "blender::StringRef":
|
||||
return StringRefPrinter(value)
|
||||
if type_name == "blender::StringRefNull":
|
||||
return StringRefPrinter(value)
|
||||
if type_name == "blender::IndexRange":
|
||||
return IndexRangePrinter(value)
|
||||
if type_name == "blender::index_mask::IndexMask":
|
||||
return IndexMaskPrinter(value)
|
||||
if type_name in (
|
||||
"blender::OffsetSpan<long, short>",
|
||||
"blender::index_mask::IndexMaskSegment",
|
||||
):
|
||||
return IndexMaskSegmentPrinter(value)
|
||||
if type_name.startswith("blender::offset_indices::OffsetIndices<"):
|
||||
return OffsetIndicesPrinter(value)
|
||||
return None
|
||||
|
||||
def __call__(self, value: gdb.Value):
|
||||
if printer := self.get_pretty_printer(value):
|
||||
return ErrorHandlingPrinter(printer)
|
||||
return None
|
||||
|
||||
|
||||
class ForeachIndexFilter:
|
||||
filename_pattern = r".*index_mask.*"
|
||||
|
||||
@staticmethod
|
||||
def frame_to_name(frame):
|
||||
function_name = frame.function()
|
||||
if function_name.startswith("blender::index_mask::IndexMask::foreach_index"):
|
||||
return "Foreach Index"
|
||||
|
||||
|
||||
class LazyFunctionEvalFilter:
|
||||
filename_pattern = r".*functions.*lazy_function.*"
|
||||
|
||||
@staticmethod
|
||||
def frame_to_name(frame):
|
||||
function_name = frame.function()
|
||||
if function_name.startswith((
|
||||
"blender::fn::lazy_function::LazyFunction::execute",
|
||||
"blender::fn::lazy_function::Executor::push_to_task_pool",
|
||||
)):
|
||||
return "Execute Lazy Function"
|
||||
|
||||
|
||||
class ThreadingFilter:
|
||||
filename_pattern = (
|
||||
r".*(include/tbb|libtbb|task_pool.cc|BLI_task.hh|task_range.cc).*"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def frame_to_name(frame):
|
||||
function_name = frame.function()
|
||||
if function_name.startswith("BLI_task_pool_work_and_wait"):
|
||||
return "Task Pool work and wait"
|
||||
if function_name.startswith(
|
||||
"tbb::internal::rml::private_worker::thread_routine"
|
||||
):
|
||||
return "TBB Worker Thread"
|
||||
if function_name.startswith("blender::threading::parallel_for"):
|
||||
return "Parallel For"
|
||||
if function_name.startswith("blender::threading::isolate_task"):
|
||||
return "Isolate Task"
|
||||
|
||||
|
||||
class StdFilter:
|
||||
filename_pattern = r".*/include/c\+\+/13.*"
|
||||
|
||||
@staticmethod
|
||||
def frame_to_name(frame):
|
||||
function_name = frame.function()
|
||||
if function_name.startswith("std::function") and "operator()" in function_name:
|
||||
return "Call std::function"
|
||||
|
||||
|
||||
class FunctionRefFilter:
|
||||
filename_pattern = r".*BLI_function_ref.hh"
|
||||
|
||||
@staticmethod
|
||||
def frame_to_name(frame):
|
||||
function_name = frame.function()
|
||||
if "operator()" in function_name:
|
||||
return "Call FunctionRef"
|
||||
|
||||
|
||||
frame_filters = [
|
||||
ForeachIndexFilter(),
|
||||
LazyFunctionEvalFilter(),
|
||||
ThreadingFilter(),
|
||||
StdFilter(),
|
||||
FunctionRefFilter(),
|
||||
]
|
||||
|
||||
|
||||
class FrameFilter:
|
||||
def __init__(self):
|
||||
self.name = "blender-frame-filters"
|
||||
self.priority = 100
|
||||
self.enabled = True
|
||||
|
||||
def filter(self, frame_iter):
|
||||
import re
|
||||
|
||||
current_filter = None
|
||||
current_frames = []
|
||||
|
||||
def handle_gathered_frames():
|
||||
nonlocal current_frames
|
||||
nonlocal current_filter
|
||||
|
||||
if current_frames:
|
||||
top_frame = current_frames[-1]
|
||||
bottom_frame = current_frames[0]
|
||||
name = current_filter.frame_to_name(top_frame)
|
||||
if name is None:
|
||||
yield from current_frames
|
||||
else:
|
||||
yield SimpleFrameDecorator(name, bottom_frame, current_frames[1:])
|
||||
|
||||
current_frames = []
|
||||
current_filter = None
|
||||
|
||||
for frame in frame_iter:
|
||||
file_name = frame.filename()
|
||||
if file_name is None:
|
||||
yield frame
|
||||
continue
|
||||
if current_filter and re.match(
|
||||
current_filter.filename_pattern, file_name
|
||||
):
|
||||
current_frames.append(frame)
|
||||
continue
|
||||
|
||||
yield from handle_gathered_frames()
|
||||
|
||||
for f in frame_filters:
|
||||
if re.match(f.filename_pattern, file_name):
|
||||
current_filter = f
|
||||
current_frames = [frame]
|
||||
break
|
||||
else:
|
||||
yield frame
|
||||
|
||||
yield from handle_gathered_frames()
|
||||
|
||||
|
||||
class SimpleFrameDecorator(FrameDecorator):
|
||||
def __init__(self, name, frame, elided_frames):
|
||||
super().__init__(frame)
|
||||
self.name = name
|
||||
self.frame = frame
|
||||
self.elided_frames = elided_frames
|
||||
|
||||
def elided(self):
|
||||
return iter(self.elided_frames)
|
||||
|
||||
def function(self):
|
||||
return self.name
|
||||
|
||||
def frame_args(self):
|
||||
return None
|
||||
|
||||
def address(self):
|
||||
return None
|
||||
|
||||
def line(self):
|
||||
return None
|
||||
|
||||
def filename(self):
|
||||
return None
|
||||
|
||||
def frame_locals(self):
|
||||
return None
|
||||
|
||||
|
||||
def register():
|
||||
gdb.printing.register_pretty_printer(None, BlenderPrettyPrinters(), replace=True)
|
||||
|
||||
frame_filter = FrameFilter()
|
||||
gdb.frame_filters[frame_filter.name] = frame_filter
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
1
blender-5.2.0/tools/debug/gdb/gdb/README.md
Normal file
1
blender-5.2.0/tools/debug/gdb/gdb/README.md
Normal file
@@ -0,0 +1 @@
|
||||
This folder contains (incomplete) type hints for the `gdb` [Python API](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Python-API.html). This simplifies working with the API.
|
||||
7
blender-5.2.0/tools/debug/gdb/gdb/__init__.py
Normal file
7
blender-5.2.0/tools/debug/gdb/gdb/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
from .gdb import *
|
||||
import printing
|
||||
import types
|
||||
224
blender-5.2.0/tools/debug/gdb/gdb/gdb.py
Normal file
224
blender-5.2.0/tools/debug/gdb/gdb/gdb.py
Normal file
@@ -0,0 +1,224 @@
|
||||
# SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
import enum
|
||||
|
||||
from collections.abc import (
|
||||
Sequence,
|
||||
)
|
||||
|
||||
|
||||
# Does not actually exist.
|
||||
class TypeCode:
|
||||
pass
|
||||
|
||||
|
||||
TYPE_CODE_PTR: TypeCode
|
||||
TYPE_CODE_ARRAY: TypeCode
|
||||
TYPE_CODE_STRUCT: TypeCode
|
||||
TYPE_CODE_UNION: TypeCode
|
||||
TYPE_CODE_ENUM: TypeCode
|
||||
TYPE_CODE_FLAGS: TypeCode
|
||||
TYPE_CODE_FUNC: TypeCode
|
||||
TYPE_CODE_INT: TypeCode
|
||||
TYPE_CODE_FLT: TypeCode
|
||||
TYPE_CODE_VOID: TypeCode
|
||||
TYPE_CODE_SET: TypeCode
|
||||
TYPE_CODE_RANGE: TypeCode
|
||||
TYPE_CODE_STRING: TypeCode
|
||||
TYPE_CODE_BITSTRING: TypeCode
|
||||
TYPE_CODE_ERROR: TypeCode
|
||||
TYPE_CODE_METHOD: TypeCode
|
||||
TYPE_CODE_METHODPTR: TypeCode
|
||||
TYPE_CODE_MEMBERPTR: TypeCode
|
||||
TYPE_CODE_REF: TypeCode
|
||||
TYPE_CODE_RVALUE_REF: TypeCode
|
||||
TYPE_CODE_CHAR: TypeCode
|
||||
TYPE_CODE_BOOL: TypeCode
|
||||
TYPE_CODE_COMPLEX: TypeCode
|
||||
TYPE_CODE_TYPEDEF: TypeCode
|
||||
TYPE_CODE_NAMESPACE: TypeCode
|
||||
TYPE_CODE_DECFLOAT: TypeCode
|
||||
TYPE_CODE_INTERNAL_FUNCTION: TypeCode
|
||||
|
||||
|
||||
class Objfile:
|
||||
pass
|
||||
|
||||
|
||||
class Type:
|
||||
alignof: int
|
||||
sizeof: int
|
||||
code: TypeCode
|
||||
dynamic: bool
|
||||
name: str | None
|
||||
tag: str | None
|
||||
objfile: Objfile | None
|
||||
|
||||
def fields(self) -> list[Field]:
|
||||
pass
|
||||
|
||||
def array(self, n1, n2=None) -> Type:
|
||||
pass
|
||||
|
||||
def vector(self, n1, n2=None) -> Type:
|
||||
pass
|
||||
|
||||
def const(self) -> Type:
|
||||
pass
|
||||
|
||||
def volatile(self) -> Type:
|
||||
pass
|
||||
|
||||
def unqualified(self) -> Type:
|
||||
pass
|
||||
|
||||
def range(self):
|
||||
pass
|
||||
|
||||
def reference(self) -> Type:
|
||||
pass
|
||||
|
||||
def pointer(self) -> Type:
|
||||
pass
|
||||
|
||||
def strip_typedefs(self) -> Type:
|
||||
pass
|
||||
|
||||
def target(self) -> Type:
|
||||
pass
|
||||
|
||||
def template_argument(self, n, block=None) -> type | Value:
|
||||
pass
|
||||
|
||||
def optimized_out(self) -> Value:
|
||||
pass
|
||||
|
||||
|
||||
class Field:
|
||||
bitpos: int
|
||||
enumval: int
|
||||
name: str | None
|
||||
artificial: bool
|
||||
is_base_class: bool
|
||||
bitsize: int
|
||||
type: Type
|
||||
parent_type: Type
|
||||
|
||||
|
||||
class Value:
|
||||
type: Type
|
||||
address: Value | None
|
||||
is_optimized_out: bool
|
||||
dynamic_type: Type
|
||||
is_lazy: bool
|
||||
|
||||
def dereference(self) -> Value:
|
||||
pass
|
||||
|
||||
def referenced_value(self) -> Value:
|
||||
pass
|
||||
|
||||
def reference_value(self) -> Value:
|
||||
pass
|
||||
|
||||
def const_value(self) -> Value:
|
||||
pass
|
||||
|
||||
def format_string(self, *args, **kwargs) -> str:
|
||||
pass
|
||||
|
||||
def string(self, encoding=None, errors=None, length=None) -> str:
|
||||
pass
|
||||
|
||||
def lazy_string(self, encoding=None, length=None) -> str:
|
||||
pass
|
||||
|
||||
def fetch_lazy(self):
|
||||
pass
|
||||
|
||||
def cast(self, type: Type) -> Value:
|
||||
pass
|
||||
|
||||
def reinterpret_cast(self, type: Type) -> Value:
|
||||
pass
|
||||
|
||||
def __getitem__(self, subscript: int | str) -> Value:
|
||||
pass
|
||||
|
||||
|
||||
def lookup_type(name: str, block=None) -> Type:
|
||||
pass
|
||||
|
||||
|
||||
def lookup_global_symbol(name: str):
|
||||
pass
|
||||
|
||||
|
||||
def execute(command: str, from_tty=None, to_string=None):
|
||||
pass
|
||||
|
||||
|
||||
def parse_and_eval(expression: str) -> Value:
|
||||
pass
|
||||
|
||||
|
||||
def set_convenience_variable(name: str, value: Value):
|
||||
pass
|
||||
|
||||
|
||||
def convenience_variable(name: str) -> Value | None:
|
||||
pass
|
||||
|
||||
|
||||
class MemoryError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Command:
|
||||
def __init__(self, name, command_class, complete_class=None, prefix=None):
|
||||
pass
|
||||
|
||||
def dont_repeat(self):
|
||||
pass
|
||||
|
||||
def invoke(self, argument: str, from_tty: bool):
|
||||
pass
|
||||
|
||||
def complete(self, text: str, word: str) -> Sequence[str] | CompleteCode:
|
||||
pass
|
||||
|
||||
|
||||
# Does not actually exist.
|
||||
class CommandCode:
|
||||
pass
|
||||
|
||||
|
||||
COMMAND_NONE: CommandCode
|
||||
COMMAND_RUNNING: CommandCode
|
||||
COMMAND_DATA: CommandCode
|
||||
COMMAND_STACK: CommandCode
|
||||
COMMAND_FILES: CommandCode
|
||||
COMMAND_SUPPORT: CommandCode
|
||||
COMMAND_STATUS: CommandCode
|
||||
COMMAND_BREAKPOINTS: CommandCode
|
||||
COMMAND_TRACEPOINTS: CommandCode
|
||||
COMMAND_TUI: CommandCode
|
||||
COMMAND_USER: CommandCode
|
||||
COMMAND_OBSCURE: CommandCode
|
||||
COMMAND_MAINTENANCE: CommandCode
|
||||
|
||||
|
||||
# Does not actually exist.
|
||||
class CompleteCode:
|
||||
pass
|
||||
|
||||
|
||||
COMPLETE_NONE: CompleteCode
|
||||
COMPLETE_FILENAME: CompleteCode
|
||||
COMPLETE_LOCATION: CompleteCode
|
||||
COMPLETE_COMMAND: CompleteCode
|
||||
COMPLETE_SYMBOL: CompleteCode
|
||||
COMPLETE_EXPRESSION: CompleteCode
|
||||
13
blender-5.2.0/tools/debug/gdb/gdb/printing.py
Normal file
13
blender-5.2.0/tools/debug/gdb/gdb/printing.py
Normal file
@@ -0,0 +1,13 @@
|
||||
# SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
from .gdb import Value
|
||||
|
||||
|
||||
class PrettyPrinter:
|
||||
def __init__(self, name: str, subprinters=None):
|
||||
pass
|
||||
|
||||
def __call__(self, value: Value):
|
||||
pass
|
||||
17
blender-5.2.0/tools/debug/gdb/gdb/types.py
Normal file
17
blender-5.2.0/tools/debug/gdb/gdb/types.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
from gdb import Type, Field
|
||||
|
||||
|
||||
def get_basic_type(type: Type) -> Type:
|
||||
pass
|
||||
|
||||
|
||||
def has_field(type: Type) -> bool:
|
||||
pass
|
||||
|
||||
|
||||
def make_enum_dict(enum_type: Type) -> dict[str, int]:
|
||||
pass
|
||||
122
blender-5.2.0/tools/debug/lldb/blender_lldb_formatter.py
Normal file
122
blender-5.2.0/tools/debug/lldb/blender_lldb_formatter.py
Normal file
@@ -0,0 +1,122 @@
|
||||
# SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
"""
|
||||
LLDB pretty printer for Blender types
|
||||
|
||||
Add to ~/.lldbinit:
|
||||
|
||||
command script import {path-to-blender-source}/tools/debug/lldb/blender_lldb_formatter.py
|
||||
|
||||
If using CodeLLDB extension for VSCode this needs to be added to the user or workspace settings.json:
|
||||
|
||||
"lldb.launch.initCommands": [ "command source ${env:HOME}/.lldbinit" ]
|
||||
"""
|
||||
|
||||
import lldb
|
||||
|
||||
|
||||
def __lldb_init_module(debugger, dict):
|
||||
debugger.HandleCommand(
|
||||
'type summary add -x "^blender::VecBase<" -F blender_lldb_formatter.math_vector_SummaryProvider'
|
||||
)
|
||||
debugger.HandleCommand(
|
||||
'type synthetic add -x "^blender::(float|double)(2|3|4)x(2|3|4)$" -l blender_lldb_formatter.math_matrix_SyntheticProvider'
|
||||
)
|
||||
debugger.HandleCommand(
|
||||
'type synthetic add -x "^blender::Vector<" -l blender_lldb_formatter.bli_vector_SyntheticProvider'
|
||||
)
|
||||
|
||||
|
||||
def math_vector_SummaryProvider(valobj, internal_dict):
|
||||
debug_values_elems = valobj.GetChildAtIndex(0).GetChildAtIndex(0).GetChildAtIndex(0).GetChildAtIndex(0)
|
||||
x = debug_values_elems.GetChildAtIndex(0).GetValue()
|
||||
y = debug_values_elems.GetChildAtIndex(1).GetValue()
|
||||
z = debug_values_elems.GetChildAtIndex(2).GetValue()
|
||||
w = debug_values_elems.GetChildAtIndex(3).GetValue()
|
||||
if w is not None:
|
||||
return f'{{{x}, {y}, {z}, {w}}}'
|
||||
if z is not None:
|
||||
return f'{{{x}, {y}, {z}}}'
|
||||
if y is not None:
|
||||
return f'{{{x}, {y}}}'
|
||||
return None
|
||||
|
||||
|
||||
class math_matrix_SyntheticProvider:
|
||||
def __init__(self, valobj, internal_dict):
|
||||
self.valobj = valobj
|
||||
|
||||
if valobj.GetChildMemberWithName('w') is not None:
|
||||
self.len = 4
|
||||
elif valobj.GetChildMemberWithName('z') is not None:
|
||||
self.len = 3
|
||||
elif valobj.GetChildMemberWithName('y') is not None:
|
||||
self.len = 2
|
||||
|
||||
def num_children(self):
|
||||
return self.len
|
||||
|
||||
def get_child_at_index(self, index):
|
||||
matrix_type_name = self.valobj.GetTypeName()
|
||||
if "float4" in matrix_type_name:
|
||||
vec_type = self.valobj.GetTarget().FindFirstType("blender::float4")
|
||||
elif "float3" in matrix_type_name:
|
||||
vec_type = self.valobj.GetTarget().FindFirstType("blender::float3")
|
||||
elif "float2" in matrix_type_name:
|
||||
vec_type = self.valobj.GetTarget().FindFirstType("blender::float2")
|
||||
elif "double4" in matrix_type_name:
|
||||
vec_type = self.valobj.GetTarget().FindFirstType("blender::double4")
|
||||
elif "double3" in matrix_type_name:
|
||||
vec_type = self.valobj.GetTarget().FindFirstType("blender::double3")
|
||||
elif "double2" in matrix_type_name:
|
||||
vec_type = self.valobj.GetTarget().FindFirstType("blender::double2")
|
||||
|
||||
if index == 0:
|
||||
return self.valobj.GetChildMemberWithName('x').Cast(vec_type)
|
||||
if index == 1:
|
||||
return self.valobj.GetChildMemberWithName('y').Cast(vec_type)
|
||||
if index == 2:
|
||||
return self.valobj.GetChildMemberWithName('z').Cast(vec_type)
|
||||
if index == 3:
|
||||
return self.valobj.GetChildMemberWithName('w').Cast(vec_type)
|
||||
return None
|
||||
|
||||
|
||||
class bli_vector_SyntheticProvider:
|
||||
def __init__(self, valobj, internal_dict):
|
||||
self.valobj = valobj
|
||||
self.begin = self.valobj.GetChildMemberWithName("begin_")
|
||||
self.end = self.valobj.GetChildMemberWithName("end_")
|
||||
self.capacity_end = self.valobj.GetChildMemberWithName("capacity_end_")
|
||||
self.data_type = self.begin.GetType().GetPointeeType()
|
||||
self.data_size = self.data_type.GetByteSize()
|
||||
|
||||
def num_children(self):
|
||||
try:
|
||||
start_val = self.begin.GetValueAsUnsigned(0)
|
||||
end_val = self.end.GetValueAsUnsigned(0)
|
||||
end_capacity_val = self.capacity_end.GetValueAsUnsigned(0)
|
||||
# Make sure nothing is NULL.
|
||||
if start_val == 0 or end_val == 0 or end_capacity_val == 0:
|
||||
return 0
|
||||
# Make sure start is less than finish.
|
||||
if start_val >= end_val:
|
||||
return 0
|
||||
# Make sure finish is less than or equal to end of storage.
|
||||
if end_val > end_capacity_val:
|
||||
return 0
|
||||
|
||||
size_in_bytes = (end_val - start_val)
|
||||
if (size_in_bytes % self.data_size) != 0:
|
||||
return 0
|
||||
return (size_in_bytes // self.data_size)
|
||||
except:
|
||||
return 0
|
||||
|
||||
def get_child_at_index(self, index):
|
||||
begin = self.valobj.GetChildMemberWithName("begin_")
|
||||
data_type = begin.GetType().GetPointeeType()
|
||||
data_size = data_type.GetByteSize()
|
||||
offset = index * data_size
|
||||
return begin.CreateChildAtOffset(f"[{index}]", offset, data_type)
|
||||
Reference in New Issue
Block a user