From 876b21f21cb9bfdff1479364f60f2ce6b15ea0b3 Mon Sep 17 00:00:00 2001 From: Sam Estep Date: Wed, 23 Jul 2025 10:00:24 -0400 Subject: Add LLDB data formatters for Slang IR (#7828) --- source/slang/slang_lldb.py | 179 +++++++++++++++++++++++++++++++++++++++++ typings/lldb.pyi | 193 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 362 insertions(+), 10 deletions(-) create mode 100644 source/slang/slang_lldb.py 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): ... -- cgit v1.2.3