mirror of https://github.com/ocornut/imgui
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			220 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			220 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Python
		
	
| # This file implements synthetic children providers and summaries for various Dear ImGui types for LLDB.
 | |
| # LLDB is used by Xcode, Android Studio, and may be used from VS Code, C++Builder, CLion, Eclipse etc.
 | |
| 
 | |
| #
 | |
| # 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})"
 | |
| 
 | |
| class ImStrvSummary(object):
 | |
| 	def __init__(self, valobj, internal_dict):
 | |
| 		self.valobj = valobj
 | |
| 
 | |
| 	def update(self):
 | |
| 		pass
 | |
| 
 | |
| 	def get_summary(self):
 | |
| 		begin = self.valobj.GetChildMemberWithName("Begin").GetValueAsUnsigned()
 | |
| 		end = self.valobj.GetChildMemberWithName("End").GetValueAsUnsigned()
 | |
| 
 | |
| 		if begin == 0:
 | |
| 			return "<null>"
 | |
| 
 | |
| 		if end < begin:
 | |
| 			return "<invalid>"
 | |
| 
 | |
| 		error = lldb.SBError()
 | |
| 		data = self.valobj.GetProcess().ReadMemory(begin, end - begin, error)
 | |
| 
 | |
| 		if not error.Success():
 | |
| 			return "<failed to read memory>"
 | |
| 
 | |
| 		# Turn the byte sequence into utf-8, escape non-printables
 | |
| 		data = data.decode("utf-8", errors="backslashreplace")
 | |
| 		data = repr(data)[1:-1]
 | |
| 
 | |
| 		return f'"{data}"'
 | |
| 
 | |
| 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)
 | |
| 	add_summary("^ImStrv", ImStrvSummary)
 |