mirror of
https://github.com/ocornut/imgui.git
synced 2026-01-04 12:27:46 +00:00
Added type formatters for the LLDB debuggers (e.g. Xcode) (#8950)
This commit is contained in:
@@ -14,3 +14,7 @@ imgui.natvis
|
||||
With this, types like ImVector<> will be displayed nicely in the debugger.
|
||||
(read comments inside file for details)
|
||||
|
||||
imgui_lldb.py
|
||||
LLDB: synthetic children provider and summaries for Dear ImGui types.
|
||||
With this, types like ImVector<> will be displayed nicely in the debugger.
|
||||
(read comments inside file for details)
|
||||
|
||||
187
misc/debuggers/imgui_lldb.py
Normal file
187
misc/debuggers/imgui_lldb.py
Normal file
@@ -0,0 +1,187 @@
|
||||
# This file implements synthetic children providers and summaries for various ImGui types for LLDB.
|
||||
#
|
||||
# Useful links/documentation related to the feature:
|
||||
# - https://lldb.llvm.org/use/variable.html#summary-strings
|
||||
# - https://lldb.llvm.org/use/variable.html#synthetic-children
|
||||
# - https://lldb.llvm.org/python_reference/lldb-module.html
|
||||
#
|
||||
# To use it in a debug session:
|
||||
# > (lldb) command script import <path-to-this-file>
|
||||
#
|
||||
# Alternatively you may include the above command in your ~/.lldbinit file to have the formatters
|
||||
# available in all future sessions
|
||||
|
||||
import lldb
|
||||
|
||||
class ArraySynthBase(object):
|
||||
"""
|
||||
Helper baseclass aimed to reduce the boilerplate needed for "array-like" containers
|
||||
"""
|
||||
|
||||
def __init__(self, valobj, internal_dict):
|
||||
self.valobj = valobj
|
||||
|
||||
def bind_to(self, pointer, size):
|
||||
array_p = pointer.GetType().GetPointeeType().GetArrayType(size).GetPointerType()
|
||||
self.array = pointer.Cast(array_p).Dereference()
|
||||
|
||||
def update(self):
|
||||
self.array = self.valobj
|
||||
|
||||
def num_children(self, max_children):
|
||||
return self.array.GetNumChildren(max_children)
|
||||
|
||||
def get_child_index(self, name):
|
||||
return self.array.GetIndexOfChildWithName(name)
|
||||
|
||||
def get_child_at_index(self, index):
|
||||
return self.array.GetChildAtIndex(index)
|
||||
|
||||
def has_children(self):
|
||||
return self.array.MightHaveChildren()
|
||||
|
||||
def get_value(self):
|
||||
return self.array
|
||||
|
||||
class ImVectorSynth(ArraySynthBase):
|
||||
def update(self):
|
||||
self.size = self.valobj.GetChildMemberWithName("Size").GetValueAsUnsigned()
|
||||
self.capacity = self.valobj.GetChildMemberWithName("Capacity").GetValueAsUnsigned()
|
||||
|
||||
data = self.valobj.GetChildMemberWithName("Data")
|
||||
|
||||
self.bind_to(data, self.size)
|
||||
|
||||
def get_summary(self):
|
||||
return f"Size={self.size} Capacity={self.capacity}"
|
||||
|
||||
class ImSpanSynth(ArraySynthBase):
|
||||
def update(self):
|
||||
data = self.valobj.GetChildMemberWithName("Data")
|
||||
end = self.valobj.GetChildMemberWithName("DataEnd")
|
||||
|
||||
element_size = data.GetType().GetPointeeType().GetByteSize()
|
||||
array_size = end.GetValueAsUnsigned() - data.GetValueAsUnsigned()
|
||||
|
||||
self.size = int(array_size / element_size)
|
||||
|
||||
self.bind_to(data, self.size)
|
||||
|
||||
def get_summary(self):
|
||||
return f"Size={self.size}"
|
||||
|
||||
class ImRectSummary(object):
|
||||
def __init__(self, valobj, internal_dict):
|
||||
self.valobj = valobj
|
||||
|
||||
def update(self):
|
||||
pass
|
||||
|
||||
def get_summary(self):
|
||||
min = self.valobj.GetChildMemberWithName("Min")
|
||||
max = self.valobj.GetChildMemberWithName("Max")
|
||||
|
||||
minX = float(min.GetChildMemberWithName("x").GetValue())
|
||||
minY = float(min.GetChildMemberWithName("y").GetValue())
|
||||
|
||||
maxX = float(max.GetChildMemberWithName("x").GetValue())
|
||||
maxY = float(max.GetChildMemberWithName("y").GetValue())
|
||||
|
||||
return f"Min=({minX}, {minY}) Max=({maxX}, {maxY}) Size=({maxX - minX}, {maxY - minY})"
|
||||
|
||||
def get_active_enum_flags(valobj):
|
||||
flag_set = set()
|
||||
|
||||
enum_name = valobj.GetType().GetName() + "_"
|
||||
enum_type = valobj.GetTarget().FindFirstType(enum_name)
|
||||
|
||||
if not enum_type.IsValid():
|
||||
return flag_set
|
||||
|
||||
enum_members = enum_type.GetEnumMembers()
|
||||
value = valobj.GetValueAsUnsigned()
|
||||
|
||||
for i in range(0, enum_members.GetSize()):
|
||||
member = enum_members.GetTypeEnumMemberAtIndex(i)
|
||||
|
||||
if value & member.GetValueAsUnsigned():
|
||||
flag_set.add(member.GetName().removeprefix(enum_name))
|
||||
|
||||
return flag_set
|
||||
|
||||
class ImGuiWindowSummary(object):
|
||||
def __init__(self, valobj, internal_dict):
|
||||
self.valobj = valobj
|
||||
|
||||
def update(self):
|
||||
pass
|
||||
|
||||
def get_summary(self):
|
||||
name = self.valobj.GetChildMemberWithName("Name").GetSummary()
|
||||
|
||||
active = self.valobj.GetChildMemberWithName("Active").GetValueAsUnsigned() != 0
|
||||
was_active = self.valobj.GetChildMemberWithName("WasActive").GetValueAsUnsigned() != 0
|
||||
hidden = self.valobj.GetChildMemberWithName("Hidden") != 0
|
||||
|
||||
flags = get_active_enum_flags(self.valobj.GetChildMemberWithName("Flags"))
|
||||
|
||||
active = 1 if active or was_active else 0
|
||||
child = 1 if "ChildWindow" in flags else 0
|
||||
popup = 1 if "Popup" in flags else 0
|
||||
hidden = 1 if hidden else 0
|
||||
|
||||
return f"Name {name} Active {active} Child {child} Popup {popup} Hidden {hidden}"
|
||||
|
||||
|
||||
def __lldb_init_module(debugger, internal_dict):
|
||||
"""
|
||||
This function will be automatically called by LLDB when the module is loaded, here
|
||||
we register the various synthetics/summaries we have build before
|
||||
"""
|
||||
|
||||
category_name = "imgui"
|
||||
category = debugger.GetCategory(category_name)
|
||||
|
||||
# Make sure we don't accidentally keep accumulating languages or override the user's
|
||||
# category enablement in Xcode, where lldb-rpc-server loads this file once for eac
|
||||
# debugging session
|
||||
if not category.IsValid():
|
||||
category = debugger.CreateCategory(category_name)
|
||||
category.AddLanguage(lldb.eLanguageTypeC_plus_plus)
|
||||
category.SetEnabled(True)
|
||||
|
||||
def add_summary(typename, impl):
|
||||
summary = None
|
||||
|
||||
if isinstance(impl, str):
|
||||
summary = lldb.SBTypeSummary.CreateWithSummaryString(impl)
|
||||
summary.SetOptions(lldb.eTypeOptionCascade)
|
||||
else:
|
||||
# Unfortunately programmatic summary string generation is an entirely different codepath
|
||||
# in LLDB. Register a convenient trampoline function which makes it look like it's part
|
||||
# of the SyntheticChildrenProvider contract
|
||||
summary = lldb.SBTypeSummary.CreateWithScriptCode(f'''
|
||||
synth = {impl.__module__}.{impl.__qualname__}(valobj.GetNonSyntheticValue(), internal_dict)
|
||||
synth.update()
|
||||
|
||||
return synth.get_summary()
|
||||
''')
|
||||
summary.SetOptions(lldb.eTypeOptionCascade | lldb.eTypeOptionFrontEndWantsDereference)
|
||||
|
||||
category.AddTypeSummary(lldb.SBTypeNameSpecifier(typename, True), summary)
|
||||
|
||||
def add_synthetic(typename, impl):
|
||||
add_summary(typename, impl)
|
||||
|
||||
synthetic = lldb.SBTypeSynthetic.CreateWithClassName(f"{impl.__module__}.{impl.__qualname__}")
|
||||
synthetic.SetOptions(lldb.eTypeOptionCascade | lldb.eTypeOptionFrontEndWantsDereference)
|
||||
|
||||
category.AddTypeSynthetic(lldb.SBTypeNameSpecifier(typename, True), synthetic)
|
||||
|
||||
add_synthetic("^ImVector<.+>$", ImVectorSynth)
|
||||
add_synthetic("^ImSpan<.+>$", ImSpanSynth)
|
||||
|
||||
add_summary("^ImVec2$", "x=${var.x} y=${var.y}")
|
||||
add_summary("^ImVec4$", "x=${var.x} y=${var.y} z=${var.z} w=${var.w}")
|
||||
add_summary("^ImRect$", ImRectSummary)
|
||||
add_summary("^ImGuiWindow$", ImGuiWindowSummary)
|
||||
Reference in New Issue
Block a user