summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSam Estep <sam@samestep.com>2025-07-23 10:00:24 -0400
committerGitHub <noreply@github.com>2025-07-23 14:00:24 +0000
commit876b21f21cb9bfdff1479364f60f2ce6b15ea0b3 (patch)
tree52ecc56939b737dfbcf82bc802642588d2995334
parented8add13e917eb50a0ab4b021b57191271313a58 (diff)
Add LLDB data formatters for Slang IR (#7828)
-rw-r--r--source/slang/slang_lldb.py179
-rw-r--r--typings/lldb.pyi193
2 files changed, 362 insertions, 10 deletions
diff --git a/source/slang/slang_lldb.py b/source/slang/slang_lldb.py
new file mode 100644
index 000000000..c1213e830
--- /dev/null
+++ b/source/slang/slang_lldb.py
@@ -0,0 +1,179 @@
+"""
+This python script provides LLDB formatters for Slang IR types.
+To use it, add the following line to your ~/.lldbinit file:
+command script import /path/to/source/slang/slang_lldb.py
+"""
+
+import json
+
+import lldb
+
+
+class Children:
+ indices: dict[str, int]
+ values: list[lldb.SBValue]
+
+ def __init__(self):
+ self.indices = {}
+ self.values = []
+
+ def append(self, value: lldb.SBValue) -> None:
+ self.indices[value.name] = len(self.values)
+ self.values.append(value)
+
+ def __len__(self) -> int:
+ return len(self.values)
+
+ def get_index(self, name: str) -> int:
+ return self.indices[name]
+
+ def get_at_index(self, idx: int) -> lldb.SBValue:
+ return self.values[idx]
+
+
+def IRUse_summary(valobj: lldb.SBValue, dict) -> str:
+ val = valobj.GetNonSyntheticValue()
+ return val.GetChildMemberWithName("usedValue").deref.summary
+
+
+class IRInstListBase_synthetic(lldb.SBSyntheticValueProvider):
+ def __init__(self, valobj: lldb.SBValue, dict):
+ self.valobj = valobj
+
+ def num_children(self):
+ return len(self.children)
+
+ def get_child_index(self, name):
+ return self.children.get_index(name)
+
+ def get_child_at_index(self, idx):
+ return self.children.get_at_index(idx)
+
+ def update(self):
+ self.children = Children()
+ pointer = self.valobj.GetChildMemberWithName("first")
+ i = 0
+ while pointer.unsigned != 0:
+ child = pointer.deref
+ self.children.append(child.Clone(f"[{i}]"))
+ pointer = child.GetNonSyntheticValue().GetChildMemberWithName("next")
+ i += 1
+
+ def has_children(self):
+ return True
+
+
+class IRInst_synthetic(lldb.SBSyntheticValueProvider):
+ def __init__(self, valobj: lldb.SBValue, dict):
+ self.valobj = valobj
+
+ def num_children(self):
+ return len(self.children)
+
+ def get_child_index(self, name):
+ return self.children.get_index(name)
+
+ def get_child_at_index(self, idx):
+ return self.children.get_at_index(idx)
+
+ def update(self):
+ self.children = Children()
+ target = self.valobj.target
+ ty = self.valobj.type
+ op = self.valobj.GetChildMemberWithName("m_op")
+
+ # literal values
+ value: list[tuple[str, lldb.SBValue]] = []
+ match op.value:
+ case "kIROp_StringLit":
+ string_lit_t = target.FindFirstType("Slang::IRStringLit")
+ string_lit = self.valobj.Cast(string_lit_t)
+ val = string_lit.GetChildMemberWithName("value")
+ value = [("[value]", val.GetChildMemberWithName("stringVal"))]
+ case "kIROp_IntLit":
+ int_lit_t = target.FindFirstType("Slang::IRIntLit")
+ int_lit = self.valobj.Cast(int_lit_t)
+ val = int_lit.GetChildMemberWithName("value")
+ value = [("[value]", val.GetChildMemberWithName("intVal"))]
+
+ # operands
+ operands: list[tuple[str, lldb.SBValue]] = []
+ offset = ty.GetByteSize()
+ ir_use_t = target.FindFirstType("Slang::IRUse")
+ ir_use_size = ir_use_t.GetByteSize()
+ for index in range(self.valobj.GetChildMemberWithName("operandCount").unsigned):
+ name = f"[operand{index}]"
+ operand = self.valobj.CreateChildAtOffset(
+ name, offset + index * ir_use_size, ir_use_t
+ )
+ operands.append((name, operand))
+
+ for name, child in [
+ ("[op]", op),
+ ("[UID]", self.valobj.GetChildMemberWithName("_debugUID")),
+ (
+ "[type]",
+ self.valobj.GetChildMemberWithName("typeUse").GetChildMemberWithName(
+ "usedValue"
+ ),
+ ),
+ # TODO: [exportName]
+ # TODO: [importName]
+ # TODO: [name]
+ *value,
+ *operands,
+ (
+ "[decorations/children]",
+ self.valobj.GetChildMemberWithName("m_decorationsAndChildren"),
+ ),
+ ("[parent]", self.valobj.GetChildMemberWithName("parent")),
+ # TODO: Traverse the linked list to show all uses next to
+ # each other, rather than pointing to the first one.
+ ("[uses]", self.valobj.GetChildMemberWithName("firstUse")),
+ ]:
+ self.children.append(child.Clone(name))
+
+ def has_children(self):
+ return True
+
+
+def IRInst_summary(valobj: lldb.SBValue, dict) -> str:
+ val = valobj.GetNonSyntheticValue()
+ op = val.GetChildMemberWithName("m_op")
+ return f"{{{op.value} {val.address_of.value}}}"
+
+
+def stringval_summary(valobj: lldb.SBValue) -> str:
+ val = valobj.GetNonSyntheticValue()
+ num_chars = val.GetChildMemberWithName("numChars").unsigned
+ chars = val.GetChildMemberWithName("chars").GetPointeeData(0, num_chars).uint8
+ return json.dumps("".join(chr(chars[i]) for i in range(num_chars)))
+
+
+def StringValue_summary(valobj: lldb.SBValue, dict) -> str:
+ return stringval_summary(valobj)
+
+
+def StringSliceValue_summary(valobj: lldb.SBValue, dict) -> str:
+ return stringval_summary(valobj)
+
+
+def __lldb_init_module(debugger: lldb.SBDebugger, internal_dict):
+ commands = [
+ # Slang::IRUse
+ "type summary add Slang::IRUse -F slang_lldb.IRUse_summary -w slang",
+ # Slang::IRInstListBase
+ "type synthetic add Slang::IRInstListBase -l slang_lldb.IRInstListBase_synthetic -w slang",
+ # Slang::IRInst
+ "type synthetic add Slang::IRInst -l slang_lldb.IRInst_synthetic -w slang",
+ "type summary add --expand Slang::IRInst -F slang_lldb.IRInst_summary -w slang",
+ # Slang::IRConstant::StringValue
+ "type summary add Slang::IRConstant::StringValue -F slang_lldb.StringValue_summary -w slang",
+ # Slang::IRConstant::StringSliceValue
+ "type summary add Slang::IRConstant::StringSliceValue -F slang_lldb.StringSliceValue_summary -w slang",
+ # Enable slang category
+ "type category enable slang",
+ ]
+
+ for c in commands:
+ debugger.HandleCommand(c)
diff --git a/typings/lldb.pyi b/typings/lldb.pyi
index 5a4eb1522..646bb46b2 100644
--- a/typings/lldb.pyi
+++ b/typings/lldb.pyi
@@ -152,6 +152,179 @@ class SBData:
class SBDebugger:
def HandleCommand(self, command: str) -> None: ...
+class SBTarget:
+ @property
+ def thisown(self): ...
+ @property
+ def eBroadcastBitBreakpointChanged(self): ...
+ @property
+ def eBroadcastBitModulesLoaded(self): ...
+ @property
+ def eBroadcastBitModulesUnloaded(self): ...
+ @property
+ def eBroadcastBitWatchpointChanged(self): ...
+ @property
+ def eBroadcastBitSymbolsLoaded(self): ...
+ @property
+ def eBroadcastBitSymbolsChanged(self): ...
+ def __init__(self, *args): ...
+ @property
+ def __swig_destroy__(self): ...
+ def __nonzero__(self): ...
+ __bool__ = __nonzero__
+ def IsValid(self): ...
+ def EventIsTargetEvent(event): ...
+ def GetTargetFromEvent(event): ...
+ def GetNumModulesFromEvent(event): ...
+ def GetModuleAtIndexFromEvent(idx, event): ...
+ def GetBroadcasterClassName(): ...
+ def GetProcess(self): ...
+ def SetCollectingStats(self, v): ...
+ def GetCollectingStats(self): ...
+ def GetStatistics(self, *args): ...
+ def GetPlatform(self): ...
+ def GetEnvironment(self): ...
+ def Install(self): ...
+ def LoadCore(self, *args): ...
+ def LaunchSimple(self, argv, envp, working_directory): ...
+ def Launch(self, *args): ...
+ def Attach(self, attach_info, error): ...
+ def AttachToProcessWithID(self, listener, pid, error): ...
+ def AttachToProcessWithName(self, listener, name, wait_for, error): ...
+ def ConnectRemote(self, listener, url, plugin_name, error): ...
+ def GetExecutable(self): ...
+ def AppendImageSearchPath(self, _from, to, error): ...
+ def AddModule(self, *args): ...
+ def GetNumModules(self): ...
+ def GetModuleAtIndex(self, idx): ...
+ def RemoveModule(self, module): ...
+ def GetDebugger(self): ...
+ def FindModule(self, file_spec): ...
+ def FindCompileUnits(self, sb_file_spec): ...
+ def GetByteOrder(self): ...
+ def GetAddressByteSize(self): ...
+ def GetTriple(self): ...
+ def GetABIName(self): ...
+ def GetLabel(self): ...
+ def SetLabel(self, label): ...
+ def GetDataByteSize(self): ...
+ def GetCodeByteSize(self): ...
+ def GetMaximumNumberOfChildrenToDisplay(self): ...
+ def SetSectionLoadAddress(self, section, section_base_addr): ...
+ def ClearSectionLoadAddress(self, section): ...
+ def SetModuleLoadAddress(self, module, sections_offset): ...
+ def ClearModuleLoadAddress(self, module): ...
+ def FindFunctions(self, *args): ...
+ def FindFirstGlobalVariable(self, name): ...
+ def FindGlobalVariables(self, *args): ...
+ def FindGlobalFunctions(self, name, max_matches, matchtype): ...
+ def Clear(self): ...
+ def ResolveFileAddress(self, file_addr): ...
+ def ResolveLoadAddress(self, vm_addr): ...
+ def ResolvePastLoadAddress(self, stop_id, vm_addr): ...
+ def ResolveSymbolContextForAddress(self, addr, resolve_scope): ...
+ def ReadMemory(self, addr, buf, error): ...
+ def BreakpointCreateByLocation(self, *args): ...
+ def BreakpointCreateByName(self, *args): ...
+ def BreakpointCreateByNames(self, *args): ...
+ def BreakpointCreateByRegex(self, *args): ...
+ def BreakpointCreateBySourceRegex(self, *args): ...
+ def BreakpointCreateForException(self, language, catch_bp, throw_bp): ...
+ def BreakpointCreateByAddress(self, address): ...
+ def BreakpointCreateBySBAddress(self, address): ...
+ def BreakpointCreateFromScript(
+ self, class_name, extra_args, module_list, file_list, request_hardware=False
+ ): ...
+ def BreakpointsCreateFromFile(self, *args): ...
+ def BreakpointsWriteToFile(self, *args): ...
+ def GetNumBreakpoints(self): ...
+ def GetBreakpointAtIndex(self, idx): ...
+ def BreakpointDelete(self, break_id): ...
+ def FindBreakpointByID(self, break_id): ...
+ def FindBreakpointsByName(self, name, bkpt_list): ...
+ def GetBreakpointNames(self, names): ...
+ def DeleteBreakpointName(self, name): ...
+ def EnableAllBreakpoints(self): ...
+ def DisableAllBreakpoints(self): ...
+ def DeleteAllBreakpoints(self): ...
+ def GetNumWatchpoints(self): ...
+ def GetWatchpointAtIndex(self, idx): ...
+ def DeleteWatchpoint(self, watch_id): ...
+ def FindWatchpointByID(self, watch_id): ...
+ def WatchAddress(self, addr, size, read, modify, error): ...
+ def WatchpointCreateByAddress(self, addr, size, options, error): ...
+ def EnableAllWatchpoints(self): ...
+ def DisableAllWatchpoints(self): ...
+ def DeleteAllWatchpoints(self): ...
+ def GetBroadcaster(self): ...
+ def FindFirstType(self, type: str) -> SBType: ...
+ def FindTypes(self, type): ...
+ def GetBasicType(self, type): ...
+ def CreateValueFromAddress(self, name, addr, type): ...
+ def CreateValueFromData(self, name, data, type): ...
+ def CreateValueFromExpression(self, name, expr): ...
+ def GetSourceManager(self): ...
+ def ReadInstructions(self, *args): ...
+ def GetInstructions(self, base_addr, buf): ...
+ def GetInstructionsWithFlavor(self, base_addr, flavor_string, buf): ...
+ def FindSymbols(self, *args): ...
+ def __eq__(self, rhs) -> bool: ...
+ def __ne__(self, rhs) -> bool: ...
+ def GetDescription(self, description, description_level): ...
+ def EvaluateExpression(self, *args): ...
+ def GetStackRedZoneSize(self): ...
+ def IsLoaded(self, module): ...
+ def GetLaunchInfo(self): ...
+ def SetLaunchInfo(self, launch_info): ...
+ def GetTrace(self): ...
+ def CreateTrace(self, error): ...
+ def __repr__(self) -> str: ...
+ def get_modules_access_object(self): ...
+ def get_modules_array(self): ...
+ def module_iter(self): ...
+ def breakpoint_iter(self): ...
+ def get_bkpts_access_object(self): ...
+ def get_target_bkpts(self): ...
+ def watchpoint_iter(self): ...
+ def get_watchpoints_access_object(self): ...
+ def get_target_watchpoints(self): ...
+ @property
+ def modules(self): ...
+ @property
+ def module(self): ...
+ @property
+ def process(self): ...
+ @property
+ def executable(self): ...
+ @property
+ def debugger(self): ...
+ @property
+ def num_breakpoints(self): ...
+ @property
+ def breakpoints(self): ...
+ @property
+ def breakpoint(self): ...
+ @property
+ def num_watchpoints(self): ...
+ @property
+ def watchpoints(self): ...
+ @property
+ def watchpoint(self): ...
+ @property
+ def broadcaster(self): ...
+ @property
+ def byte_order(self): ...
+ @property
+ def addr_size(self): ...
+ @property
+ def triple(self): ...
+ @property
+ def data_byte_size(self): ...
+ @property
+ def code_byte_size(self): ...
+ @property
+ def platform(self): ...
+
class SBType:
@property
def thisown(self): ...
@@ -293,7 +466,7 @@ class SBValue:
def GetTypeFilter(self): ...
def GetTypeSynthetic(self): ...
def CreateChildAtOffset(self, name: str, offset: int, type: SBType) -> SBValue: ...
- def Cast(self, type): ...
+ def Cast(self, type: SBType) -> SBValue: ...
def CreateValueFromExpression(self, *args): ...
def CreateValueFromAddress(self, name, address, type): ...
def CreateValueFromData(self, name, data, type): ...
@@ -307,7 +480,7 @@ class SBValue:
def GetPointeeData(self, item_idx: int = 0, item_count: int = 1) -> SBData: ...
def GetData(self): ...
def SetData(self, data, error): ...
- def Clone(self, new_name): ...
+ def Clone(self, new_name: str) -> SBValue: ...
def GetDeclaration(self): ...
def MightHaveChildren(self): ...
def IsRuntimeSupportValue(self): ...
@@ -339,9 +512,9 @@ class SBValue:
@property
def child(self): ...
@property
- def name(self): ...
+ def name(self) -> str: ...
@property
- def type(self): ...
+ def type(self) -> SBType: ...
@property
def size(self): ...
@property
@@ -349,7 +522,7 @@ class SBValue:
@property
def format(self): ...
@property
- def value(self): ...
+ def value(self) -> str: ...
@property
def value_type(self): ...
@property
@@ -361,13 +534,13 @@ class SBValue:
@property
def addr(self): ...
@property
- def deref(self): ...
+ def deref(self) -> SBValue: ...
@property
- def address_of(self): ...
+ def address_of(self) -> SBValue: ...
@property
def error(self): ...
@property
- def summary(self): ...
+ def summary(self) -> str: ...
@property
def description(self): ...
@property
@@ -375,7 +548,7 @@ class SBValue:
@property
def location(self): ...
@property
- def target(self): ...
+ def target(self) -> SBTarget: ...
@property
def process(self): ...
@property
@@ -385,7 +558,7 @@ class SBValue:
@property
def num_children(self): ...
@property
- def unsigned(self): ...
+ def unsigned(self) -> int: ...
@property
def signed(self): ...
def get_expr_path(self): ...