summary refs log tree commit diff stats
path: root/tools/debug/nimlldb.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/debug/nimlldb.py')
-rw-r--r--tools/debug/nimlldb.py1380
1 files changed, 1380 insertions, 0 deletions
diff --git a/tools/debug/nimlldb.py b/tools/debug/nimlldb.py
new file mode 100644
index 000000000..4bc4e771f
--- /dev/null
+++ b/tools/debug/nimlldb.py
@@ -0,0 +1,1380 @@
+import lldb
+from collections import OrderedDict
+from typing import Union
+
+
+def sbvaluegetitem(self: lldb.SBValue, name: Union[int, str]) -> lldb.SBValue:
+    if isinstance(name, str):
+        return self.GetChildMemberWithName(name)
+    else:
+        return self.GetChildAtIndex(name)
+
+
+# Make this easier to work with
+lldb.SBValue.__getitem__ = sbvaluegetitem
+
+NIM_IS_V2 = True
+
+
+def get_nti(value: lldb.SBValue, nim_name=None):
+    name_split = value.type.name.split("_")
+    type_nim_name = nim_name or name_split[1]
+    id_string = name_split[-1].split(" ")[0]
+
+    type_info_name = "NTI" + type_nim_name.lower() + "__" + id_string + "_"
+    nti = value.target.FindFirstGlobalVariable(type_info_name)
+    if not nti.IsValid():
+        type_info_name = "NTI" + "__" + id_string + "_"
+        nti = value.target.FindFirstGlobalVariable(type_info_name)
+    if not nti.IsValid():
+        print(f"NimEnumPrinter: lookup global symbol: '{type_info_name}' failed for {value.type.name}.\n")
+    return type_nim_name, nti
+
+
+def enum_to_string(value: lldb.SBValue, int_val=None, nim_name=None):
+    tname = nim_name or value.type.name.split("_")[1]
+
+    enum_val = value.signed
+    if int_val is not None:
+        enum_val = int_val
+
+    default_val = f"{tname}.{str(enum_val)}"
+
+    fn_syms = value.target.FindFunctions("reprEnum")
+    if not fn_syms.GetSize() > 0:
+        return default_val
+
+    fn_sym: lldb.SBSymbolContext = fn_syms.GetContextAtIndex(0)
+
+    fn: lldb.SBFunction = fn_sym.function
+
+    fn_type: lldb.SBType = fn.type
+    arg_types: lldb.SBTypeList = fn_type.GetFunctionArgumentTypes()
+    if arg_types.GetSize() < 2:
+        return default_val
+
+    arg1_type: lldb.SBType = arg_types.GetTypeAtIndex(0)
+    arg2_type: lldb.SBType = arg_types.GetTypeAtIndex(1)
+
+    ty_info_name, nti = get_nti(value, nim_name=tname)
+
+    if not nti.IsValid():
+        return default_val
+
+    call = f"{fn.name}(({arg1_type.name}){enum_val}, ({arg2_type.name})" + str(nti.GetLoadAddress()) + ");"
+
+    res = executeCommand(call)
+
+    if res.error.fail:
+        return default_val
+
+    return f"{tname}.{res.summary[1:-1]}"
+
+
+def to_string(value: lldb.SBValue):
+    # For getting NimStringDesc * value
+    value = value.GetNonSyntheticValue()
+
+    # Check if data pointer is Null
+    if value.type.is_pointer and value.unsigned == 0:
+        return None
+
+    size = int(value["Sup"]["len"].unsigned)
+
+    if size == 0:
+        return ""
+
+    if size > 2**14:
+        return "... (too long) ..."
+
+    data = value["data"]
+
+    # Check if first element is NULL
+    base_data_type = value.target.FindFirstType("char")
+    cast = data.Cast(base_data_type)
+
+    if cast.unsigned == 0:
+        return None
+
+    cast = data.Cast(value.target.FindFirstType("char").GetArrayType(size))
+    return bytearray(cast.data.uint8s).decode("utf-8")
+
+
+def to_stringV2(value: lldb.SBValue):
+    # For getting NimStringV2 value
+    value = value.GetNonSyntheticValue()
+
+    data = value["p"]["data"]
+
+    # Check if data pointer is Null
+    if value["p"].unsigned == 0:
+        return None
+
+    size = int(value["len"].signed)
+
+    if size == 0:
+        return ""
+
+    if size > 2**14:
+        return "... (too long) ..."
+
+    # Check if first element is NULL
+    base_data_type = data.type.GetArrayElementType().GetTypedefedType()
+    cast = data.Cast(base_data_type)
+
+    if cast.unsigned == 0:
+        return None
+
+    cast = data.Cast(base_data_type.GetArrayType(size))
+    return bytearray(cast.data.uint8s).decode("utf-8")
+
+
+def NimString(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    if NIM_IS_V2:
+        res = to_stringV2(value)
+    else:
+        res = to_string(value)
+
+    if res is not None:
+        return f'"{res}"'
+    else:
+        return "nil"
+
+
+def rope_helper(value: lldb.SBValue) -> str:
+    value = value.GetNonSyntheticValue()
+    if value.type.is_pointer and value.unsigned == 0:
+        return ""
+
+    if value["length"].unsigned == 0:
+        return ""
+
+    if NIM_IS_V2:
+        str_val = to_stringV2(value["data"])
+    else:
+        str_val = to_string(value["data"])
+
+    if str_val is None:
+        str_val = ""
+
+    return rope_helper(value["left"]) + str_val + rope_helper(value["right"])
+
+
+def Rope(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    rope_str = rope_helper(value)
+
+    if len(rope_str) == 0:
+        rope_str = "nil"
+    else:
+        rope_str = f'"{rope_str}"'
+
+    return f"Rope({rope_str})"
+
+
+def NCSTRING(value: lldb.SBValue, internal_dict=None):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    ty = value.Dereference().type
+    val = value.target.CreateValueFromAddress(
+        value.name or "temp", lldb.SBAddress(value.unsigned, value.target), ty
+    ).AddressOf()
+    return val.summary
+
+
+def ObjectV2(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    orig_value = value.GetNonSyntheticValue()
+    if orig_value.type.is_pointer and orig_value.unsigned == 0:
+        return "nil"
+
+    custom_summary = get_custom_summary(value)
+    if custom_summary is not None:
+        return custom_summary
+
+    while orig_value.type.is_pointer:
+        orig_value = orig_value.Dereference()
+
+    if "_" in orig_value.type.name:
+        obj_name = orig_value.type.name.split("_")[1].replace("colonObjectType", "")
+    else:
+        obj_name = orig_value.type.name
+
+    num_children = value.num_children
+    fields = []
+
+    for i in range(num_children):
+        fields.append(f"{value[i].name}: {value[i].summary}")
+
+    res = f"{obj_name}(" + ", ".join(fields) + ")"
+    return res
+
+
+def Number(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    if value.type.is_pointer and value.signed == 0:
+        return "nil"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    return str(value.signed)
+
+
+def Float(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    return str(value.value)
+
+
+def UnsignedNumber(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    return str(value.unsigned)
+
+
+def Bool(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    return str(value.value)
+
+
+def CharArray(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    return str([f"'{char}'" for char in value.uint8s])
+
+
+def Array(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    value = value.GetNonSyntheticValue()
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    value = value.GetNonSyntheticValue()
+    return "[" + ", ".join([value[i].summary for i in range(value.num_children)]) + "]"
+
+
+def Tuple(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    while value.type.is_pointer:
+        value = value.Dereference()
+
+    num_children = value.num_children
+
+    fields = []
+
+    for i in range(num_children):
+        key = value[i].name
+        val = value[i].summary
+        if key.startswith("Field"):
+            fields.append(f"{val}")
+        else:
+            fields.append(f"{key}: {val}")
+
+    return "(" + ", ".join(fields) + f")"
+
+
+def is_local(value: lldb.SBValue) -> bool:
+    line: lldb.SBLineEntry = value.frame.GetLineEntry()
+    decl: lldb.SBDeclaration = value.GetDeclaration()
+
+    if line.file == decl.file and decl.line != 0:
+        return True
+
+    return False
+
+
+def is_in_scope(value: lldb.SBValue) -> bool:
+    line: lldb.SBLineEntry = value.frame.GetLineEntry()
+    decl: lldb.SBDeclaration = value.GetDeclaration()
+
+    if is_local(value) and decl.line < line.line:
+        return True
+
+    return False
+
+
+def Enum(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_value_summary(value)
+    if custom_summary is not None:
+        return custom_summary
+
+    return enum_to_string(value)
+
+
+def EnumSet(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    vals = []
+    max_vals = 7
+    for child in value.children:
+        vals.append(child.summary)
+        if len(vals) > max_vals:
+            vals.append("...")
+            break
+
+    return "{" + ", ".join(vals) + "}"
+
+
+def Set(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if custom_summary is not None:
+        return custom_summary
+
+    vals = []
+    max_vals = 7
+    for child in value.children:
+        vals.append(child.value)
+        if len(vals) > max_vals:
+            vals.append("...")
+            break
+
+    return "{" + ", ".join(vals) + "}"
+
+
+def Table(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if custom_summary is not None:
+        return custom_summary
+
+    fields = []
+
+    for i in range(value.num_children):
+        key = value[i].name
+        val = value[i].summary
+        fields.append(f"{key}: {val}")
+
+    return "Table({" + ", ".join(fields) + "})"
+
+
+def HashSet(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if custom_summary is not None:
+        return custom_summary
+
+    fields = []
+
+    for i in range(value.num_children):
+        fields.append(f"{value[i].summary}")
+
+    return "HashSet({" + ", ".join(fields) + "})"
+
+
+def StringTable(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    fields = []
+
+    for i in range(value.num_children - 1):
+        key = value[i].name
+        val = value[i].summary
+        fields.append(f"{key}: {val}")
+
+    mode = value[value.num_children - 1].summary
+
+    return "StringTable({" + ", ".join(fields) + f"}}, mode={mode})"
+
+
+def Sequence(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    return "@[" + ", ".join([value[i].summary for i in range(value.num_children)]) + "]"
+
+
+class StringChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.data_type: lldb.SBType
+        if not NIM_IS_V2:
+            self.data_type = self.value.target.FindFirstType("char")
+
+        self.first_element: lldb.SBValue
+        self.update()
+        self.count = 0
+
+    def num_children(self):
+        return self.count
+
+    def get_child_index(self, name):
+        return int(name.lstrip("[").rstrip("]"))
+
+    def get_child_at_index(self, index):
+        offset = index * self.data_size
+        return self.first_element.CreateChildAtOffset("[" + str(index) + "]", offset, self.data_type)
+
+    def get_data(self) -> lldb.SBValue:
+        return self.value["p"]["data"] if NIM_IS_V2 else self.value["data"]
+
+    def get_len(self) -> int:
+        if NIM_IS_V2:
+            if self.value["p"].unsigned == 0:
+                return 0
+
+            size = int(self.value["len"].signed)
+
+            if size == 0:
+                return 0
+
+            data = self.value["p"]["data"]
+
+            # Check if first element is NULL
+            base_data_type = data.type.GetArrayElementType().GetTypedefedType()
+            cast = data.Cast(base_data_type)
+
+            if cast.unsigned == 0:
+                return 0
+        else:
+            if self.value.type.is_pointer and self.value.unsigned == 0:
+                return 0
+
+            size = int(self.value["Sup"]["len"].unsigned)
+
+            if size == 0:
+                return 0
+
+            data = self.value["data"]
+
+            # Check if first element is NULL
+            base_data_type = self.value.target.FindFirstType("char")
+            cast = data.Cast(base_data_type)
+
+            if cast.unsigned == 0:
+                return 0
+
+        return size
+
+    def update(self):
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+
+        data = self.get_data()
+        size = self.get_len()
+
+        self.count = size
+        self.first_element = data
+
+        if NIM_IS_V2:
+            self.data_type = data.type.GetArrayElementType().GetTypedefedType()
+
+        self.data_size = self.data_type.GetByteSize()
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class ArrayChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.data_type: lldb.SBType
+        self.first_element: lldb.SBValue
+        self.update()
+
+    def num_children(self):
+        return self.has_children() and self.value.num_children
+
+    def get_child_index(self, name: str):
+        return int(name.lstrip("[").rstrip("]"))
+
+    def get_child_at_index(self, index):
+        offset = index * self.value[index].GetByteSize()
+        return self.first_element.CreateChildAtOffset("[" + str(index) + "]", offset, self.data_type)
+
+    def update(self):
+        if not self.has_children():
+            return
+
+        self.first_element = self.value[0]
+        self.data_type = self.value.type.GetArrayElementType()
+
+    def has_children(self):
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return False
+        return bool(self.value.num_children)
+
+
+class SeqChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.data_type: lldb.SBType
+        self.first_element: lldb.SBValue
+        self.data: lldb.SBValue
+        self.count = 0
+        self.update()
+
+    def num_children(self):
+        return self.count
+
+    def get_child_index(self, name: str):
+        return int(name.lstrip("[").rstrip("]"))
+
+    def get_child_at_index(self, index):
+        offset = index * self.data[index].GetByteSize()
+        return self.first_element.CreateChildAtOffset("[" + str(index) + "]", offset, self.data_type)
+
+    def get_data(self) -> lldb.SBValue:
+        return self.value["p"]["data"] if NIM_IS_V2 else self.value["data"]
+
+    def get_len(self) -> lldb.SBValue:
+        return self.value["len"] if NIM_IS_V2 else self.value["Sup"]["len"]
+
+    def update(self):
+        self.count = 0
+
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+
+        self.count = self.get_len().unsigned
+
+        if not self.has_children():
+            return
+
+        data = self.get_data()
+        self.data_type = data.type.GetArrayElementType()
+
+        self.data = data.Cast(self.data_type.GetArrayType(self.num_children()))
+        self.first_element = self.data
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class ObjectChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.data_type: lldb.SBType
+        self.first_element: lldb.SBValue
+        self.data: lldb.SBValue
+        self.children: OrderedDict[str, int] = OrderedDict()
+        self.child_list: list[lldb.SBValue] = []
+        self.update()
+
+    def num_children(self):
+        return len(self.children)
+
+    def get_child_index(self, name: str):
+        return self.children[name]
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def populate_children(self):
+        self.children.clear()
+        self.child_list = []
+
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+
+        stack = [self.value.GetNonSyntheticValue()]
+
+        index = 0
+
+        while stack:
+            cur_val = stack.pop()
+            if cur_val.type.is_pointer and cur_val.unsigned == 0:
+                continue
+
+            while cur_val.type.is_pointer:
+                cur_val = cur_val.Dereference()
+
+            # Add super objects if they exist
+            if cur_val.num_children > 0 and cur_val[0].name == "Sup" and cur_val[0].type.name.startswith("tyObject"):
+                stack.append(cur_val[0])
+
+            for child in cur_val.children:
+                child = child.GetNonSyntheticValue()
+                if child.name == "Sup":
+                    continue
+                self.children[child.name] = index
+                self.child_list.append(child)
+                index += 1
+
+    def update(self):
+        self.populate_children()
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class HashSetChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.child_list: list[lldb.SBValue] = []
+        self.update()
+
+    def num_children(self):
+        return len(self.child_list)
+
+    def get_child_index(self, name: str):
+        return int(name.lstrip("[").rstrip("]"))
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def get_data(self) -> lldb.SBValue:
+        return self.value["data"]["p"]["data"] if NIM_IS_V2 else self.value["data"]["data"]
+
+    def get_len(self) -> lldb.SBValue:
+        return self.value["data"]["len"] if NIM_IS_V2 else self.value["data"]["Sup"]["len"]
+
+    def update(self):
+        self.child_list = []
+
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+
+        tuple_len = int(self.get_len().unsigned)
+        tuple = self.get_data()
+
+        base_data_type = tuple.type.GetArrayElementType()
+
+        cast = tuple.Cast(base_data_type.GetArrayType(tuple_len))
+
+        index = 0
+        for i in range(tuple_len):
+            el = cast[i]
+            field0 = int(el[0].unsigned)
+            if field0 == 0:
+                continue
+            key = el[1]
+            child = key.CreateValueFromAddress(f"[{str(index)}]", key.GetLoadAddress(), key.GetType())
+            index += 1
+
+            self.child_list.append(child)
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class SetCharChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.ty = self.value.target.FindFirstType("char")
+        self.child_list: list[lldb.SBValue] = []
+        self.update()
+
+    def num_children(self):
+        return len(self.child_list)
+
+    def get_child_index(self, name: str):
+        return int(name.lstrip("[").rstrip("]"))
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def update(self):
+        self.child_list = []
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+
+        cur_pos = 0
+        for child in self.value.children:
+            child_val = child.signed
+            if child_val != 0:
+                temp = child_val
+                num_bits = 8
+                while temp != 0:
+                    is_set = temp & 1
+                    if is_set == 1:
+                        data = lldb.SBData.CreateDataFromInt(cur_pos)
+                        child = self.value.synthetic_child_from_data(f"[{len(self.child_list)}]", data, self.ty)
+                        self.child_list.append(child)
+                    temp = temp >> 1
+                    cur_pos += 1
+                    num_bits -= 1
+                cur_pos += num_bits
+            else:
+                cur_pos += 8
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+def create_set_children(value: lldb.SBValue, child_type: lldb.SBType, starting_pos: int) -> list[lldb.SBValue]:
+    child_list: list[lldb.SBValue] = []
+    cur_pos = starting_pos
+
+    if value.num_children > 0:
+        children = value.children
+    else:
+        children = [value]
+
+    for child in children:
+        child_val = child.signed
+        if child_val != 0:
+            temp = child_val
+            num_bits = 8
+            while temp != 0:
+                is_set = temp & 1
+                if is_set == 1:
+                    data = lldb.SBData.CreateDataFromInt(cur_pos)
+                    child = value.synthetic_child_from_data(f"[{len(child_list)}]", data, child_type)
+                    child_list.append(child)
+                temp = temp >> 1
+                cur_pos += 1
+                num_bits -= 1
+            cur_pos += num_bits
+        else:
+            cur_pos += 8
+
+    return child_list
+
+
+class SetIntChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.ty = self.value.target.FindFirstType(f"NI64")
+        self.child_list: list[lldb.SBValue] = []
+        self.update()
+
+    def num_children(self):
+        return len(self.child_list)
+
+    def get_child_index(self, name: str):
+        return int(name.lstrip("[").rstrip("]"))
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def update(self):
+        self.child_list = []
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+        bits = self.value.GetByteSize() * 8
+        starting_pos = -(bits // 2)
+        self.child_list = create_set_children(self.value, self.ty, starting_pos)
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class SetUIntChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.ty = self.value.target.FindFirstType(f"NU64")
+        self.child_list: list[lldb.SBValue] = []
+        self.update()
+
+    def num_children(self):
+        return len(self.child_list)
+
+    def get_child_index(self, name: str):
+        return int(name.lstrip("[").rstrip("]"))
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def update(self):
+        self.child_list = []
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+        self.child_list = create_set_children(self.value, self.ty, starting_pos=0)
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class SetEnumChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.ty = self.value.target.FindFirstType(self.value.type.name.replace("tySet_", ""))
+        self.child_list: list[lldb.SBValue] = []
+        self.update()
+
+    def num_children(self):
+        return len(self.child_list)
+
+    def get_child_index(self, name: str):
+        return int(name.lstrip("[").rstrip("]"))
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def update(self):
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+        self.child_list = create_set_children(self.value, self.ty, starting_pos=0)
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class TableChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.children: OrderedDict[str, int] = OrderedDict()
+        self.child_list: list[lldb.SBValue] = []
+
+        self.update()
+
+    def num_children(self):
+        return len(self.child_list)
+
+    def get_child_index(self, name: str):
+        return self.children[name]
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def get_data(self) -> lldb.SBValue:
+        return self.value["data"]["p"]["data"] if NIM_IS_V2 else self.value["data"]["data"]
+
+    def get_len(self) -> lldb.SBValue:
+        return self.value["data"]["len"] if NIM_IS_V2 else self.value["data"]["Sup"]["len"]
+
+    def update(self):
+        self.child_list = []
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+
+        tuple_len = int(self.get_len().unsigned)
+        tuple = self.get_data()
+
+        base_data_type = tuple.type.GetArrayElementType()
+
+        cast = tuple.Cast(base_data_type.GetArrayType(tuple_len))
+
+        index = 0
+        for i in range(tuple_len):
+            el = cast[i]
+            field0 = int(el[0].unsigned)
+            if field0 == 0:
+                continue
+            key = el[1]
+            val = el[2]
+            key_summary = key.summary
+            child = self.value.CreateValueFromAddress(key_summary, val.GetLoadAddress(), val.GetType())
+            self.child_list.append(child)
+            self.children[key_summary] = index
+            index += 1
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class StringTableChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.children: OrderedDict[str, int] = OrderedDict()
+        self.child_list: list[lldb.SBValue] = []
+        self.update()
+
+    def num_children(self):
+        return len(self.child_list)
+
+    def get_child_index(self, name: str):
+        return self.children[name]
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def get_data(self) -> lldb.SBValue:
+        return self.value["data"]["p"]["data"] if NIM_IS_V2 else self.value["data"]["data"]
+
+    def get_len(self) -> lldb.SBValue:
+        return self.value["data"]["len"] if NIM_IS_V2 else self.value["data"]["Sup"]["len"]
+
+    def update(self):
+        self.children.clear()
+        self.child_list = []
+
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+
+        tuple_len = int(self.get_len().unsigned)
+        tuple = self.get_data()
+
+        base_data_type = tuple.type.GetArrayElementType()
+
+        cast = tuple.Cast(base_data_type.GetArrayType(tuple_len))
+
+        index = 0
+        for i in range(tuple_len):
+            el = cast[i]
+            field0 = int(el[2].unsigned)
+            if field0 == 0:
+                continue
+            key = el[0]
+            val = el[1]
+            child = val.CreateValueFromAddress(key.summary, val.GetLoadAddress(), val.GetType())
+            self.child_list.append(child)
+            self.children[key.summary] = index
+            index += 1
+
+        self.child_list.append(self.value["mode"])
+        self.children["mode"] = index
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class LLDBDynamicObjectProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        value = value.GetNonSyntheticValue()
+        self.value: lldb.SBValue = value[0]
+        self.children: OrderedDict[str, int] = OrderedDict()
+        self.child_list: list[lldb.SBValue] = []
+
+        while self.value.type.is_pointer:
+            self.value = self.value.Dereference()
+
+        self.update()
+
+    def num_children(self):
+        return len(self.child_list)
+
+    def get_child_index(self, name: str):
+        return self.children[name]
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def update(self):
+        self.children.clear()
+        self.child_list = []
+
+        for i, child in enumerate(self.value.children):
+            name = child.name.strip('"')
+            new_child = child.CreateValueFromAddress(name, child.GetLoadAddress(), child.GetType())
+
+            self.children[name] = i
+            self.child_list.append(new_child)
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class LLDBBasicObjectProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value: lldb.SBValue = value
+
+    def num_children(self):
+        if self.value is not None:
+            return self.value.num_children
+        return 0
+
+    def get_child_index(self, name: str):
+        return self.value.GetIndexOfChildWithName(name)
+
+    def get_child_at_index(self, index):
+        return self.value.GetChildAtIndex(index)
+
+    def update(self):
+        pass
+
+    def has_children(self):
+        return self.num_children() > 0
+
+
+class CustomObjectChildrenProvider:
+    """
+    This children provider handles values returned from lldbDebugSynthetic*
+    Nim procedures
+    """
+
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value: lldb.SBValue = get_custom_synthetic(value) or value
+        if "lldbdynamicobject" in self.value.type.name.lower():
+            self.provider = LLDBDynamicObjectProvider(self.value, internalDict)
+        else:
+            self.provider = LLDBBasicObjectProvider(self.value, internalDict)
+
+    def num_children(self):
+        return self.provider.num_children()
+
+    def get_child_index(self, name: str):
+        return self.provider.get_child_index(name)
+
+    def get_child_at_index(self, index):
+        return self.provider.get_child_at_index(index)
+
+    def update(self):
+        self.provider.update()
+
+    def has_children(self):
+        return self.provider.has_children()
+
+
+def echo(debugger: lldb.SBDebugger, command: str, result, internal_dict):
+    debugger.HandleCommand("po " + command)
+
+
+SUMMARY_FUNCTIONS: dict[str, lldb.SBFunction] = {}
+SYNTHETIC_FUNCTIONS: dict[str, lldb.SBFunction] = {}
+
+
+def get_custom_summary(value: lldb.SBValue) -> Union[str, None]:
+    """Get a custom summary if a function exists for it"""
+    value = value.GetNonSyntheticValue()
+    if value.GetAddress().GetOffset() == 0:
+        return None
+
+    base_type = get_base_type(value.type)
+
+    fn = SUMMARY_FUNCTIONS.get(base_type.name)
+    if fn is None:
+        return None
+
+    fn_type: lldb.SBType = fn.type
+
+    arg_types: lldb.SBTypeList = fn_type.GetFunctionArgumentTypes()
+    first_type = arg_types.GetTypeAtIndex(0)
+
+    while value.type.is_pointer:
+        value = value.Dereference()
+
+    if first_type.is_pointer:
+        command = f"{fn.name}(({first_type.name})" + str(value.GetLoadAddress()) + ");"
+    else:
+        command = f"{fn.name}(*({first_type.GetPointerType().name})" + str(value.GetLoadAddress()) + ");"
+
+    res = executeCommand(command)
+
+    if res.error.fail:
+        return None
+
+    return res.summary.strip('"')
+
+
+def get_custom_value_summary(value: lldb.SBValue) -> Union[str, None]:
+    """Get a custom summary if a function exists for it"""
+
+    fn: lldb.SBFunction = SUMMARY_FUNCTIONS.get(value.type.name)
+    if fn is None:
+        return None
+
+    command = f"{fn.name}(({value.type.name})" + str(value.signed) + ");"
+    res = executeCommand(command)
+
+    if res.error.fail:
+        return None
+
+    return res.summary.strip('"')
+
+
+def get_custom_synthetic(value: lldb.SBValue) -> Union[lldb.SBValue, None]:
+    """Get a custom synthetic object if a function exists for it"""
+    value = value.GetNonSyntheticValue()
+    if value.GetAddress().GetOffset() == 0:
+        return None
+
+    base_type = get_base_type(value.type)
+
+    fn = SYNTHETIC_FUNCTIONS.get(base_type.name)
+    if fn is None:
+        return None
+
+    fn_type: lldb.SBType = fn.type
+
+    arg_types: lldb.SBTypeList = fn_type.GetFunctionArgumentTypes()
+    first_type = arg_types.GetTypeAtIndex(0)
+
+    while value.type.is_pointer:
+        value = value.Dereference()
+
+    if first_type.is_pointer:
+        first_arg = f"({first_type.name}){value.GetLoadAddress()}"
+    else:
+        first_arg = f"*({first_type.GetPointerType().name}){value.GetLoadAddress()}"
+
+    if arg_types.GetSize() > 1 and fn.GetArgumentName(1) == "Result":
+        ret_type = arg_types.GetTypeAtIndex(1)
+        ret_type = get_base_type(ret_type)
+
+        command = f"""
+            {ret_type.name} lldbT;
+            nimZeroMem((void*)(&lldbT), sizeof({ret_type.name}));
+            {fn.name}(({first_arg}), (&lldbT));
+            lldbT;
+        """
+    else:
+        command = f"{fn.name}({first_arg});"
+
+    res = executeCommand(command)
+
+    if res.error.fail:
+        print(res.error)
+        return None
+
+    return res
+
+
+def get_base_type(ty: lldb.SBType) -> lldb.SBType:
+    """Get the base type of the type"""
+    temp = ty
+    while temp.IsPointerType():
+        temp = temp.GetPointeeType()
+    return temp
+
+
+def use_base_type(ty: lldb.SBType) -> bool:
+    types_to_check = [
+        "NF",
+        "NF32",
+        "NF64",
+        "NI",
+        "NI8",
+        "NI16",
+        "NI32",
+        "NI64",
+        "bool",
+        "NIM_BOOL",
+        "NU",
+        "NU8",
+        "NU16",
+        "NU32",
+        "NU64",
+    ]
+
+    for type_to_check in types_to_check:
+        if ty.name.startswith(type_to_check):
+            return False
+
+    return True
+
+
+def breakpoint_function_wrapper(frame: lldb.SBFrame, bp_loc, internal_dict):
+    """This allows function calls to Nim for custom object summaries and synthetic children"""
+    debugger = lldb.debugger
+
+    global SUMMARY_FUNCTIONS
+    global SYNTHETIC_FUNCTIONS
+
+    global NIM_IS_V2
+
+    for tname, fn in SYNTHETIC_FUNCTIONS.items():
+        debugger.HandleCommand(f"type synthetic delete -w nim {tname}")
+
+    SUMMARY_FUNCTIONS = {}
+    SYNTHETIC_FUNCTIONS = {}
+
+    target: lldb.SBTarget = debugger.GetSelectedTarget()
+
+    NIM_IS_V2 = target.FindFirstType("TNimTypeV2").IsValid()
+
+    module = frame.GetSymbolContext(lldb.eSymbolContextModule).module
+
+    for sym in module:
+        if (
+            not sym.name.startswith("lldbDebugSummary")
+            and not sym.name.startswith("lldbDebugSynthetic")
+            and not sym.name.startswith("dollar___")
+        ):
+            continue
+
+        fn_syms: lldb.SBSymbolContextList = target.FindFunctions(sym.name)
+        if not fn_syms.GetSize() > 0:
+            continue
+
+        fn_sym: lldb.SBSymbolContext = fn_syms.GetContextAtIndex(0)
+
+        fn: lldb.SBFunction = fn_sym.function
+        fn_type: lldb.SBType = fn.type
+        arg_types: lldb.SBTypeList = fn_type.GetFunctionArgumentTypes()
+
+        if arg_types.GetSize() > 1 and fn.GetArgumentName(1) == "Result":
+            pass # don't continue
+        elif arg_types.GetSize() != 1:
+            continue
+
+        arg_type: lldb.SBType = arg_types.GetTypeAtIndex(0)
+        if use_base_type(arg_type):
+            arg_type = get_base_type(arg_type)
+
+        if sym.name.startswith("lldbDebugSummary") or sym.name.startswith("dollar___"):
+            SUMMARY_FUNCTIONS[arg_type.name] = fn
+        elif sym.name.startswith("lldbDebugSynthetic"):
+            SYNTHETIC_FUNCTIONS[arg_type.name] = fn
+            debugger.HandleCommand(
+                f"type synthetic add -w nim -l {__name__}.CustomObjectChildrenProvider {arg_type.name}"
+            )
+
+
+def executeCommand(command, *args):
+    debugger = lldb.debugger
+    process = debugger.GetSelectedTarget().GetProcess()
+    frame: lldb.SBFrame = process.GetSelectedThread().GetSelectedFrame()
+
+    expr_options = lldb.SBExpressionOptions()
+    expr_options.SetIgnoreBreakpoints(False)
+    expr_options.SetFetchDynamicValue(lldb.eDynamicCanRunTarget)
+    expr_options.SetTimeoutInMicroSeconds(30 * 1000 * 1000)  # 30 second timeout
+    expr_options.SetTryAllThreads(True)
+    expr_options.SetUnwindOnError(False)
+    expr_options.SetGenerateDebugInfo(True)
+    expr_options.SetLanguage(lldb.eLanguageTypeC)
+    expr_options.SetCoerceResultToId(True)
+    res = frame.EvaluateExpression(command, expr_options)
+
+    return res
+
+
+def __lldb_init_module(debugger, internal_dict):
+    # fmt: off
+    debugger.HandleCommand(f"breakpoint command add -F {__name__}.breakpoint_function_wrapper --script-type python 1")
+    debugger.HandleCommand(f"type summary add -w nim -n sequence -F  {__name__}.Sequence -x tySequence_+[[:alnum:]]+$")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SeqChildrenProvider -x tySequence_+[[:alnum:]]+$")
+
+    debugger.HandleCommand(f"type summary add -w nim -n chararray -F  {__name__}.CharArray -x char\s+[\d+]")
+    debugger.HandleCommand(f"type summary add -w nim -n array -F  {__name__}.Array -x tyArray_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.ArrayChildrenProvider -x tyArray_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n string -F  {__name__}.NimString NimStringDesc")
+
+    debugger.HandleCommand(f"type summary add -w nim -n stringv2 -F {__name__}.NimString -x NimStringV2$")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.StringChildrenProvider -x NimStringV2$")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.StringChildrenProvider -x NimStringDesc$")
+
+    debugger.HandleCommand(f"type summary add -w nim -n cstring -F  {__name__}.NCSTRING NCSTRING")
+
+    debugger.HandleCommand(f"type summary add -w nim -n object -F  {__name__}.ObjectV2 -x tyObject_+[[:alnum:]]+_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.ObjectChildrenProvider -x tyObject_+[[:alnum:]]+_+[[:alnum:]]+$")
+
+    debugger.HandleCommand(f"type summary add -w nim -n tframe -F  {__name__}.ObjectV2 -x TFrame$")
+
+    debugger.HandleCommand(f"type summary add -w nim -n rootobj -F  {__name__}.ObjectV2 -x RootObj$")
+
+    debugger.HandleCommand(f"type summary add -w nim -n enum -F  {__name__}.Enum -x tyEnum_+[[:alnum:]]+_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n hashset -F  {__name__}.HashSet -x tyObject_+HashSet_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.HashSetChildrenProvider -x tyObject_+HashSet_+[[:alnum:]]+")
+
+    debugger.HandleCommand(f"type summary add -w nim -n rope -F  {__name__}.Rope -x tyObject_+Rope[[:alnum:]]+_+[[:alnum:]]+")
+
+    debugger.HandleCommand(f"type summary add -w nim -n setuint -F  {__name__}.Set -x tySet_+tyInt_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SetIntChildrenProvider -x tySet_+tyInt[0-9]+_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n setint -F  {__name__}.Set -x tySet_+tyInt[0-9]+_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n setuint2 -F  {__name__}.Set -x tySet_+tyUInt[0-9]+_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SetUIntChildrenProvider -x tySet_+tyUInt[0-9]+_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SetUIntChildrenProvider -x tySet_+tyInt_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n setenum -F  {__name__}.EnumSet -x tySet_+tyEnum_+[[:alnum:]]+_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SetEnumChildrenProvider -x tySet_+tyEnum_+[[:alnum:]]+_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n setchar -F  {__name__}.Set -x tySet_+tyChar_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SetCharChildrenProvider -x tySet_+tyChar_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n table -F  {__name__}.Table -x tyObject_+Table_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.TableChildrenProvider -x tyObject_+Table_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n stringtable -F  {__name__}.StringTable -x tyObject_+StringTableObj_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.StringTableChildrenProvider -x tyObject_+StringTableObj_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n tuple2 -F  {__name__}.Tuple -x tyObject_+Tuple_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n tuple -F  {__name__}.Tuple -x tyTuple_+[[:alnum:]]+")
+
+    debugger.HandleCommand(f"type summary add -w nim -n float -F  {__name__}.Float NF")
+    debugger.HandleCommand(f"type summary add -w nim -n float32 -F  {__name__}.Float NF32")
+    debugger.HandleCommand(f"type summary add -w nim -n float64 -F  {__name__}.Float NF64")
+    debugger.HandleCommand(f"type summary add -w nim -n integer -F  {__name__}.Number -x NI")
+    debugger.HandleCommand(f"type summary add -w nim -n integer8 -F  {__name__}.Number -x NI8")
+    debugger.HandleCommand(f"type summary add -w nim -n integer16 -F  {__name__}.Number -x NI16")
+    debugger.HandleCommand(f"type summary add -w nim -n integer32 -F  {__name__}.Number -x NI32")
+    debugger.HandleCommand(f"type summary add -w nim -n integer64 -F  {__name__}.Number -x NI64")
+    debugger.HandleCommand(f"type summary add -w nim -n bool -F  {__name__}.Bool -x bool")
+    debugger.HandleCommand(f"type summary add -w nim -n bool2 -F  {__name__}.Bool -x NIM_BOOL")
+    debugger.HandleCommand(f"type summary add -w nim -n uinteger -F  {__name__}.UnsignedNumber -x NU")
+    debugger.HandleCommand(f"type summary add -w nim -n uinteger8 -F  {__name__}.UnsignedNumber -x NU8")
+    debugger.HandleCommand(f"type summary add -w nim -n uinteger16 -F  {__name__}.UnsignedNumber -x NU16")
+    debugger.HandleCommand(f"type summary add -w nim -n uinteger32 -F  {__name__}.UnsignedNumber -x NU32")
+    debugger.HandleCommand(f"type summary add -w nim -n uinteger64 -F  {__name__}.UnsignedNumber -x NU64")
+    debugger.HandleCommand("type category enable nim")
+    debugger.HandleCommand(f"command script add -f  {__name__}.echo echo")
+    # fmt: on