imgui_lldb.py 6.1 KB

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