6.1 KB、189行 生 Blame 履歴
1# This file implements synthetic children providers and summaries for various Dear ImGui types for LLDB.
2# LLDB is used by Xcode, Android Studio, and may be used from VS Code, C++Builder, CLion, Eclipse etc.
3
4#
5# Useful links/documentation related to the feature:
6# - https://lldb.llvm.org/use/variable.html#summary-strings
7# - https://lldb.llvm.org/use/variable.html#synthetic-children
8# - https://lldb.llvm.org/python_reference/lldb-module.html
9#
10# To use it in a debug session:
11# > (lldb) command script import <path-to-this-file>
12#
13# Alternatively you may include the above command in your ~/.lldbinit file to have the formatters
14# available in all future sessions
15
16import lldb
17
18class ArraySynthBase(object):
19 """
20 Helper baseclass aimed to reduce the boilerplate needed for "array-like" containers
21 """
22
23 def __init__(self, valobj, internal_dict):
24 self.valobj = valobj
25
26 def bind_to(self, pointer, size):
27 array_p = pointer.GetType().GetPointeeType().GetArrayType(size).GetPointerType()
28 self.array = pointer.Cast(array_p).Dereference()
29
30 def update(self):
31 self.array = self.valobj
32
33 def num_children(self, max_children):
34 return self.array.GetNumChildren(max_children)
35
36 def get_child_index(self, name):
37 return self.array.GetIndexOfChildWithName(name)
38
39 def get_child_at_index(self, index):
40 return self.array.GetChildAtIndex(index)
41
42 def has_children(self):
43 return self.array.MightHaveChildren()
44
45 def get_value(self):
46 return self.array
47
48class ImVectorSynth(ArraySynthBase):
49 def update(self):
50 self.size = self.valobj.GetChildMemberWithName("Size").GetValueAsUnsigned()
51 self.capacity = self.valobj.GetChildMemberWithName("Capacity").GetValueAsUnsigned()
52
53 data = self.valobj.GetChildMemberWithName("Data")
54
55 self.bind_to(data, self.size)
56
57 def get_summary(self):
58 return f"Size={self.size} Capacity={self.capacity}"
59
60class ImSpanSynth(ArraySynthBase):
61 def update(self):
62 data = self.valobj.GetChildMemberWithName("Data")
63 end = self.valobj.GetChildMemberWithName("DataEnd")
64
65 element_size = data.GetType().GetPointeeType().GetByteSize()
66 array_size = end.GetValueAsUnsigned() - data.GetValueAsUnsigned()
67
68 self.size = int(array_size / element_size)
69
70 self.bind_to(data, self.size)
71
72 def get_summary(self):
73 return f"Size={self.size}"
74
75class ImRectSummary(object):
76 def __init__(self, valobj, internal_dict):
77 self.valobj = valobj
78
79 def update(self):
80 pass
81
82 def get_summary(self):
83 min = self.valobj.GetChildMemberWithName("Min")
84 max = self.valobj.GetChildMemberWithName("Max")
85
86 minX = float(min.GetChildMemberWithName("x").GetValue())
87 minY = float(min.GetChildMemberWithName("y").GetValue())
88
89 maxX = float(max.GetChildMemberWithName("x").GetValue())
90 maxY = float(max.GetChildMemberWithName("y").GetValue())
91
92 return f"Min=({minX}, {minY}) Max=({maxX}, {maxY}) Size=({maxX - minX}, {maxY - minY})"
93
94def get_active_enum_flags(valobj):
95 flag_set = set()
96
97 enum_name = valobj.GetType().GetName() + "_"
98 enum_type = valobj.GetTarget().FindFirstType(enum_name)
99
100 if not enum_type.IsValid():
101 return flag_set
102
103 enum_members = enum_type.GetEnumMembers()
104 value = valobj.GetValueAsUnsigned()
105
106 for i in range(0, enum_members.GetSize()):
107 member = enum_members.GetTypeEnumMemberAtIndex(i)
108
109 if value & member.GetValueAsUnsigned():
110 flag_set.add(member.GetName().removeprefix(enum_name))
111
112 return flag_set
113
114class ImGuiWindowSummary(object):
115 def __init__(self, valobj, internal_dict):
116 self.valobj = valobj
117
118 def update(self):
119 pass
120
121 def get_summary(self):
122 name = self.valobj.GetChildMemberWithName("Name").GetSummary()
123
124 active = self.valobj.GetChildMemberWithName("Active").GetValueAsUnsigned() != 0
125 was_active = self.valobj.GetChildMemberWithName("WasActive").GetValueAsUnsigned() != 0
126 hidden = self.valobj.GetChildMemberWithName("Hidden") != 0
127
128 flags = get_active_enum_flags(self.valobj.GetChildMemberWithName("Flags"))
129
130 active = 1 if active or was_active else 0
131 child = 1 if "ChildWindow" in flags else 0
132 popup = 1 if "Popup" in flags else 0
133 hidden = 1 if hidden else 0
134
135 return f"Name {name} Active {active} Child {child} Popup {popup} Hidden {hidden}"
136
137
138def __lldb_init_module(debugger, internal_dict):
139 """
140 This function will be automatically called by LLDB when the module is loaded, here
141 we register the various synthetics/summaries we have build before
142 """
143
144 category_name = "imgui"
145 category = debugger.GetCategory(category_name)
146
147 # Make sure we don't accidentally keep accumulating languages or override the user's
148 # category enablement in Xcode, where lldb-rpc-server loads this file once for eac
149 # debugging session
150 if not category.IsValid():
151 category = debugger.CreateCategory(category_name)
152 category.AddLanguage(lldb.eLanguageTypeC_plus_plus)
153 category.SetEnabled(True)
154
155 def add_summary(typename, impl):
156 summary = None
157
158 if isinstance(impl, str):
159 summary = lldb.SBTypeSummary.CreateWithSummaryString(impl)
160 summary.SetOptions(lldb.eTypeOptionCascade)
161 else:
162 # Unfortunately programmatic summary string generation is an entirely different codepath
163 # in LLDB. Register a convenient trampoline function which makes it look like it's part
164 # of the SyntheticChildrenProvider contract
165 summary = lldb.SBTypeSummary.CreateWithScriptCode(f'''
166 synth = {impl.__module__}.{impl.__qualname__}(valobj.GetNonSyntheticValue(), internal_dict)
167 synth.update()
168
169 return synth.get_summary()
170 ''')
171 summary.SetOptions(lldb.eTypeOptionCascade | lldb.eTypeOptionFrontEndWantsDereference)
172
173 category.AddTypeSummary(lldb.SBTypeNameSpecifier(typename, True), summary)
174
175 def add_synthetic(typename, impl):
176 add_summary(typename, impl)
177
178 synthetic = lldb.SBTypeSynthetic.CreateWithClassName(f"{impl.__module__}.{impl.__qualname__}")
179 synthetic.SetOptions(lldb.eTypeOptionCascade | lldb.eTypeOptionFrontEndWantsDereference)
180
181 category.AddTypeSynthetic(lldb.SBTypeNameSpecifier(typename, True), synthetic)
182
183 add_synthetic("^ImVector<.+>$", ImVectorSynth)
184 add_synthetic("^ImSpan<.+>$", ImSpanSynth)
185
186 add_summary("^ImVec2$", "x=${var.x} y=${var.y}")
187 add_summary("^ImVec4$", "x=${var.x} y=${var.y} z=${var.z} w=${var.w}")
188 add_summary("^ImRect$", ImRectSummary)
189 add_summary("^ImGuiWindow$", ImGuiWindowSummary)