From 77576c960388f0ad6712f1e15b97ca689892d708 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 16 Oct 2026 18:36:25 -0400 Subject: [PATCH] lldb/pretty_printers.py: support new `MultiArrayList.pointer_stability` Also fix unwrapped printing of reified union types. --- lib/lldb/pretty_printers.py | 149 ++++++++++++++++++++++++------------ src/InternPool.zig | 3 + 2 files changed, 102 insertions(+), 50 deletions(-) diff --git a/lib/lldb/pretty_printers.py b/lib/lldb/pretty_printers.py index 8246b583607273384aec2d5779dd500acc45da65..54c5e91327d5a3c04a26d9bae606d1b14cab76e4 100644 --- a/lib/lldb/pretty_printers.py +++ b/lib/lldb/pretty_printers.py @@ -13,34 +13,73 @@ page_size = 1 << 12 def log2_int(i): return i.bit_length() - 1 -def create_struct(parent, name, struct_type, inits): - struct_bytes, struct_data = bytearray(struct_type.size), lldb.SBData() - for field in struct_type.fields: - field_size = field.type.size - field_init = inits[field.name] - if isinstance(field_init, int): - match struct_data.byte_order: - case lldb.eByteOrderLittle: - byte_order = 'little' - case lldb.eByteOrderBig: - byte_order = 'big' - field_bytes = field_init.to_bytes(field_size, byte_order, signed=field.type.GetTypeFlags() & lldb.eTypeIsSigned != 0) - elif isinstance(field_init, lldb.SBValue): - field_bytes = field_init.data.uint8 - else: return - match struct_data.byte_order: +class ValueInit: + def __init__(self, value: lldb.SBValue): self.value = value + def toBytes(self, parent: lldb.SBValue, value_type: lldb.SBType) -> bytes: + return self.value.data.uint8 + def create(self, parent: lldb.SBValue, name: str, value_type: lldb.SBType) -> lldb.SBValue: + return self.value.Clone(name) + +class FromBytes(ValueInit): + def __init__(self, value_bytes: bytes): self.value_bytes = value_bytes + def toBytes(self, parent: lldb.SBValue, value_type: lldb.SBType) -> bytes: return self.value_bytes + def create(self, parent: lldb.SBValue, name: str, value_type: lldb.SBType) -> lldb.SBValue: + data = lldb.SBData() + error = lldb.SBError() + value_bytes = self.toBytes(parent, value_type); + target = parent.target + data.SetData(error, value_bytes, target.byte_order, target.addr_size) + return parent.CreateValueFromData(name, data, value_type) + +class IntLiteral(FromBytes): + def __init__(self, int_value: int): self.int_value = int_value + def toBytes(self, parent: lldb.SBValue, int_type: lldb.SBType) -> bytes: + int_flags = int_type.GetTypeFlags() + assert int_flags & lldb.eTypeIsInteger != 0 + int_is_signed = int_flags & lldb.eTypeIsSigned != 0 + match parent.target.byte_order: case lldb.eByteOrderLittle: - field_bytes = field_bytes[:field_size] - field_start = field.byte_offset - struct_bytes[field_start:field_start + len(field_bytes)] = field_bytes + byte_order = 'little' case lldb.eByteOrderBig: - field_bytes = field_bytes[-field_size:] - field_end = field.byte_offset + field_size - struct_bytes[field_end - len(field_bytes):field_end] = field_bytes - struct_data.SetData(lldb.SBError(), struct_bytes, struct_data.byte_order, struct_data.GetAddressByteSize()) - return parent.CreateValueFromData(name, struct_data, struct_type) + byte_order = 'big' + return self.int_value.to_bytes(int_type.size, byte_order, signed=int_is_signed) -# Define Zig Language +class FromBackingInt(FromBytes): + def __init__(self, field_value: int): self.field_value = field_value + def fieldValue(self, enum_type: lldb.SBType) -> int: return self.field_value + def toBytes(self, parent: lldb.SBValue, enum_type: lldb.SBType) -> bytes: + assert enum_type.type == lldb.eTypeClassEnumeration + backing_int = IntLiteral(self.fieldValue(enum_type)); + return backing_int.toBytes(parent, enum_type.GetEnumerationIntegerType()) + +class EnumField(FromBackingInt): + def __init__(self, field_index: int): self.field_index = field_index + def fieldIndex(self, enum_type: lldb.SBType) -> int: return self.field_index + def fieldValue(self, enum_type: lldb.SBType) -> int: + enum_is_signed = enum_type.GetEnumerationIntegerType().GetTypeFlags() & lldb.eTypeIsSigned != 0 + field = enum_type.enum_members[self.fieldIndex(enum_type)] + return field.signed if enum_is_signed else field.unsigned + +class EnumLiteral(EnumField): + def __init__(self, field_name: str): self.field_name = field_name + def fieldName(self, enum_type: lldb.SBType) -> str: return self.field_name + def fieldIndex(self, enum_type: lldb.SBType) -> int: + field_name = self.fieldName(enum_type) + return next(field_index for field_index, field in enumerate(enum_type.enum_members) + if field.name == field_name) + +class StructInit(FromBytes): + def __init__(self, field_inits: dict[str, ValueInit]): self.field_inits = field_inits + def toBytes(self, parent: lldb.SBValue, struct_type: lldb.SBType) -> bytes: + assert struct_type.type == lldb.eTypeClassStruct + struct_bytes = bytearray(struct_type.size) + for field in struct_type.fields: + field_bytes = self.field_inits[field.name].toBytes(parent, field.type) + assert len(field_bytes) == field.type.size + struct_bytes[field.byte_offset:field.byte_offset + len(field_bytes)] = field_bytes + return bytes(struct_bytes) + +# Define Zig Language (for programs compiled with the llvm backend) zig_keywords = { 'addrspace', @@ -204,7 +243,7 @@ class zig_TaggedUnion_SynthProvider: except: return -1 def get_child_at_index(self, index): return (self.tag, self.payload)[index] if index in range(2) else None -# Define Zig Standard Library +# Define Zig Standard Library (for programs compiled with the llvm backend) class std_MultiArrayList_SynthProvider: def __init__(self, value, _=None): self.value = value @@ -331,7 +370,7 @@ class std_Entry_SynthProvider: def get_child_index(self, name): return self.indices.get(name) def get_child_at_index(self, index): return self.children[index].deref if index in range(len(self.children)) else None -# Define Zig Compiler +# Define Zig Compiler (compiled with the llvm backend) class TagAndPayload_SynthProvider: def __init__(self, value, _=None): self.value = value @@ -435,8 +474,9 @@ class InternPool_Index_SynthProvider: def __init__(self, value, _=None): self.value = value def update(self): try: + target = self.value.target index_type = self.value.type - for helper in self.value.target.FindFunctions('%s.dbHelper' % index_type.name, lldb.eFunctionNameTypeFull): + for helper in target.FindFunctions('%s.dbHelper' % index_type.name, lldb.eFunctionNameTypeFull): ptr_self_type, ptr_tag_to_encoding_map_type = helper.function.type.GetFunctionArgumentTypes() if ptr_self_type.GetPointeeType() == index_type: break else: return @@ -465,7 +505,7 @@ class InternPool_Index_SynthProvider: for trailing_field in encoding_field.type.fields: trailing_data.Append(extra.GetChildAtIndex(extra_index).address_of.data) trailing_len = dynamic_values['trailing.%s.len' % trailing_field.name].unsigned - trailing_data.Append(lldb.SBData.CreateDataFromInt(trailing_len, trailing_data.GetAddressByteSize())) + trailing_data.Append(lldb.SBData.CreateDataFromInt(trailing_len, target.addr_size, target=target)) extra_index += trailing_len self.trailing = self.data.CreateValueFromData('trailing', trailing_data, encoding_field.type) else: @@ -685,8 +725,18 @@ value_tag_handlers = { class root_InternPool_Local_List_SynthProvider: def __init__(self, value, _=None): self.value = value def update(self): - capacity = self.value.EvaluateExpression('@as(*@This().Header, @alignCast(@ptrCast(@this().bytes - @This().bytes_offset))).capacity') - self.view = create_struct(self.value, '.view', self.value.type.FindDirectNestedType('View'), { 'bytes': self.value.GetChildMemberWithName('bytes'), 'len': capacity, 'capacity': capacity }).GetNonSyntheticValue() + capacity = IntLiteral(self.value.EvaluateExpression( + '@as(*@This().Header, @alignCast(@ptrCast(@this().bytes - @This().bytes_offset))).capacity', + ).unsigned) + View = self.value.type.FindDirectNestedType('View') + self.view = StructInit({ + 'bytes': ValueInit(self.value.GetChildMemberWithName('bytes')), + 'len': capacity, + 'capacity': capacity, + 'pointer_stability': StructInit({ + 'state': EnumField(0), + }), + }).create(self.value, '.view', View).GetNonSyntheticValue() def has_children(self): return True def num_children(self): return 1 def get_child_index(self, name): @@ -719,7 +769,10 @@ class root_InternPool_Index_SynthProvider: unwrapped_type = self.value.type.FindDirectNestedType('Unwrapped') ip = self.value.CreateValueFromType(unwrapped_type).GetChildMemberWithName('debug_state').GetChildMemberWithName('intern_pool').GetNonSyntheticValue().GetChildMemberWithName('?') tid_shift_30 = ip.GetChildMemberWithName('tid_shift_30').unsigned - self.unwrapped = create_struct(self.value, '.unwrapped', unwrapped_type, { 'tid': wrapped >> tid_shift_30, 'index': wrapped & (1 << tid_shift_30) - 1 }) + self.unwrapped = StructInit({ + 'tid': FromBackingInt(wrapped >> tid_shift_30), + 'index': IntLiteral(wrapped & (1 << tid_shift_30) - 1), + }).create(self.value, '.unwrapped', unwrapped_type) def has_children(self): return True def num_children(self): return 0 def get_child_index(self, name): @@ -732,6 +785,7 @@ class root_InternPool_Index_SynthProvider: class root_InternPool_Index_Unwrapped_SynthProvider: def __init__(self, value, _=None): self.value = value def update(self): + target = self.value.target self.tag, self.index, self.data, self.payload, self.trailing = None, None, None, None, None index = self.value.GetChildMemberWithName('index') ip = self.value.CreateValueFromType(self.value.type).GetChildMemberWithName('debug_state').GetChildMemberWithName('intern_pool').GetNonSyntheticValue().GetChildMemberWithName('?') @@ -742,36 +796,34 @@ class root_InternPool_Index_Unwrapped_SynthProvider: encoding_index, encoding_data, encoding_payload, encoding_trailing, encoding_config = encoding.GetChildMemberWithName('index'), encoding.GetChildMemberWithName('data'), encoding.GetChildMemberWithName('payload'), encoding.GetChildMemberWithName('trailing'), encoding.GetChildMemberWithName('config') if encoding_index: index_type = encoding_index.GetValueAsType() - index_bytes, index_data = index.data.uint8, lldb.SBData() - match index_data.byte_order: + index_bytes = index.data.uint8 + match target.byte_order: case lldb.eByteOrderLittle: index_bytes = bytes(index_bytes[:index_type.size]) case lldb.eByteOrderBig: index_bytes = bytes(index_bytes[-index_type.size:]) - index_data.SetData(lldb.SBError(), index_bytes, index_data.byte_order, index_data.GetAddressByteSize()) - self.index = self.value.CreateValueFromData('.index', index_data, index_type) + self.index = FromBytes(index_bytes).create(self.value, '.index', index_type) elif encoding_data: data_type = encoding_data.GetValueAsType() - data_bytes, data_data = item_data.data.uint8, lldb.SBData() - match data_data.byte_order: + data_bytes = item_data.data.uint8 + match target.byte_order: case lldb.eByteOrderLittle: data_bytes = bytes(data_bytes[:data_type.size]) case lldb.eByteOrderBig: data_bytes = bytes(data_bytes[-data_type.size:]) - data_data.SetData(lldb.SBError(), data_bytes, data_data.byte_order, data_data.GetAddressByteSize()) - self.data = self.value.CreateValueFromData('.data', data_data, data_type) + self.data = FromBytes(data_bytes).create(self.value, '.data', data_type) elif encoding_payload: extra = shared.GetChildMemberWithName('extra').GetChildMemberWithName('view').GetChildMemberWithName('0') extra_index = item_data.unsigned payload_type = encoding_payload.GetValueAsType() payload_fields = dict() for payload_field in payload_type.fields: - payload_fields[payload_field.name] = extra.child[extra_index] + payload_fields[payload_field.name] = ValueInit(extra.child[extra_index]) extra_index += 1 - self.payload = create_struct(self.value, '.payload', payload_type, payload_fields) + self.payload = StructInit(payload_fields).create(self.value, '.payload', payload_type) if encoding_trailing and encoding_config: trailing_type = encoding_trailing.GetValueAsType() - trailing_bytes, trailing_data = bytearray(trailing_type.size), lldb.SBData() + trailing_bytes = bytearray(trailing_type.size) def eval_config(config_name): expr = encoding_config.GetChildMemberWithName(config_name).value.removeprefix('.').removeprefix('@"').removesuffix('"').replace(r'\"', '"') if 'payload.' in expr: @@ -780,13 +832,11 @@ class root_InternPool_Index_Unwrapped_SynthProvider: field_type, field_byte_offset = trailing_type, 0 expr_parts = expr.split('.') for expr_part in expr_parts[1:]: - field = next(filter(lambda field: field.name == expr_part, field_type.fields)) + field = next(field for field in field_type.fields if field.name == expr_part) field_type = field.type field_byte_offset += field.byte_offset - field_data = lldb.SBData() field_bytes = trailing_bytes[field_byte_offset:field_byte_offset + field_type.size] - field_data.SetData(lldb.SBError(), field_bytes, field_data.byte_order, field_data.GetAddressByteSize()) - return self.value.CreateValueFromData('.%s' % expr_parts[-1], field_data, field_type) + return FromBytes(field_bytes).create(self.value, '.%s' % expr_parts[-1], field_type) else: return self.value.frame.EvaluateExpression(expr) for trailing_field in trailing_type.fields: @@ -812,7 +862,7 @@ class root_InternPool_Index_Unwrapped_SynthProvider: trailing_bytes[ptr_field_byte_offset:ptr_field_byte_offset + ptr_field.type.size] = extra.child[extra_index].address_of.data.uint8 len_field_value = eval_config('%s.len' % trailing_field_name) len_field_size = len_field.type.size - match trailing_data.byte_order: + match target.byte_order: case lldb.eByteOrderLittle: len_field_bytes = len_field_value.data.uint8[:len_field_size] trailing_bytes[len_field_byte_offset:len_field_byte_offset + len(len_field_bytes)] = len_field_bytes @@ -827,8 +877,7 @@ class root_InternPool_Index_Unwrapped_SynthProvider: trailing_bytes[trailing_field_byte_offset + offset:trailing_field_byte_offset + offset + 4] = extra.child[extra_index].data.uint8 extra_index += 1 break - trailing_data.SetData(lldb.SBError(), trailing_bytes, trailing_data.byte_order, trailing_data.GetAddressByteSize()) - self.trailing = self.value.CreateValueFromData('.trailing', trailing_data, trailing_type) + self.trailing = FromBytes(trailing_bytes).create(self.value, '.trailing', trailing_type) def has_children(self): return True def num_children(self): return 1 + ((self.index or self.data or self.payload) is not None) + (self.trailing is not None) def get_child_index(self, name): diff --git a/src/InternPool.zig b/src/InternPool.zig index 88c454fc2f3b414710886a524c80f4ecc7b09134..ad7c65bff970b9dc7a5666c56949312fef15663e 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -5255,6 +5255,7 @@ pub const Tag = enum(u8) { type_hash: ?u64, captures_len: ?u32, captures: ?[]CaptureValue, + reified_field_name: ?[]NullTerminatedString, field_types: []Index, field_aligns: ?[]Alignment, }, @@ -5263,6 +5264,8 @@ pub const Tag = enum(u8) { .@"trailing.captures_len.?" = .@"payload.flags.any_captures == .true", .@"trailing.captures.?" = .@"payload.flags.any_captures == .true", .@"trailing.captures.?.len" = .@"trailing.captures_len.?", + .@"trailing.reified_field_name.?" = .@"payload.flags.any_captures == .reified", + .@"trailing.reified_field_name.?.len" = .@"payload.fields_len", .@"trailing.field_types.len" = .@"payload.fields_len", .@"trailing.field_aligns.?" = .@"payload.flags.any_field_aligns", .@"trailing.field_aligns.?.len" = .@"(payload.fields_len + 3) & ~@as(u32, 3)", -- 2.54.0