diff --git a/base/runtime/odin_debugger.py b/base/runtime/odin_debugger.py index ff16b7a87..38974387a 100644 --- a/base/runtime/odin_debugger.py +++ b/base/runtime/odin_debugger.py @@ -111,6 +111,19 @@ def odin_kind(type_name, field_names, data_is_pointer, data_is_array, data_targe return None +def composite_type(name): + # the element of a pointer, multi-pointer or fixed array type named by a `typeid` and the array's count, + # which is None for a pointer; None for other types + if name.startswith("^"): + return name[1:], None + if name.startswith("[^]"): + return name[3:], None + m = re.fullmatch(r"\[([0-9]+)\](.+)", name) + if m: + return m.group(2), int(m.group(1)) + return None + + def soa_fields(field_names): return [n for n in field_names if n not in ("__$len", "__$cap", "allocator", "_")] @@ -278,6 +291,16 @@ if gdb is not None: return None def _gdb_odin_type(name): + composite = composite_type(name) + if composite is not None: + elem_name, count = composite + elem = _gdb_odin_type(elem_name) + if elem is None: + return None + if count is None: + return elem.pointer() + return elem.array(count - 1) + basic = ODIN_BASIC_TYPES.get(name) if basic is None: return _gdb_type(name, "struct " + name, "union " + name, "enum " + name) @@ -481,7 +504,11 @@ if gdb is not None: return "<%s>" % e if name is None or (tag is None and int(self._val[name]) == 0): return "nil" - return self._val[name] + value = self._val[name] + if value.type.strip_typedefs().code == gdb.TYPE_CODE_PTR: + # gdb prints nothing for a pointer value returned by `to_string` + return value.format_string() + return value class _GdbAny(_GdbPrinter): # The value it holds, after the name of its type; an unknown type shows the `any` itself @@ -569,8 +596,19 @@ if lldb is not None: cell_data_size = _lldb_fields(cell)["v"].GetByteSize() return cell_layout(elem.GetByteSize(), cell.GetByteSize(), cell_data_size) - def _lldb_odin_type(target, name, id): - # the type a `typeid` names, None when it cannot be found + def _lldb_odin_type(target, basis, name, id): + # the type a `typeid` names, None when it cannot be found; basic types come from the type system of `basis`, + # an Odin type, as the target may answer from another, such as Swift's, whose types make no value + composite = composite_type(name) + if composite is not None and typeid_hash(name) == id: + elem_name, count = composite + elem = _lldb_odin_type(target, basis, elem_name, typeid_hash(elem_name)) + if elem is None: + return None + if count is None: + return elem.GetPointerType() + return elem.GetArrayType(count) + basic = ODIN_BASIC_TYPES.get(name) if basic is None: types = target.FindTypes(name) @@ -585,11 +623,11 @@ if lldb is not None: size = target.GetAddressByteSize() if kind == "rawptr": - return target.GetBasicType(lldb.eBasicTypeVoid).GetPointerType() + return basis.GetBasicType(lldb.eBasicTypeVoid).GetPointerType() if kind == "cstring": - return target.GetBasicType(lldb.eBasicTypeChar).GetPointerType() + return basis.GetBasicType(lldb.eBasicTypeChar).GetPointerType() if kind == "cstring16": - return target.GetBasicType(lldb.eBasicTypeChar16).GetPointerType() + return basis.GetBasicType(lldb.eBasicTypeChar16).GetPointerType() basics = { ("int", 1): lldb.eBasicTypeSignedChar, @@ -611,7 +649,7 @@ if lldb is not None: basic_type = basics.get((kind, size)) if basic_type is None: return None - return target.GetBasicType(basic_type) + return basis.GetBasicType(basic_type) def lldb_is_string(sbtype, internal_dict): return _lldb_kind(sbtype) in ("string", "string16") @@ -657,10 +695,10 @@ if lldb is not None: name = id.GetValue() or "" if data == 0 or id.GetValueAsUnsigned() == 0: return "nil", None - t = _lldb_odin_type(valobj.GetTarget(), name, id.GetValueAsUnsigned()) + t = _lldb_odin_type(valobj.GetTarget(), v.GetType(), name, id.GetValueAsUnsigned()) if t is None: return name, None - if name not in ODIN_BASIC_TYPES: + if name not in ODIN_BASIC_TYPES and composite_type(name) is None: name = t.GetName() value = valobj.CreateValueFromAddress("value", data, t) value.SetPreferSyntheticValue(True) diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index 50d6fd1d7..db06de68d 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -884,6 +884,14 @@ gb_internal LLVMMetadataRef lb_debug_typeid_enum(lbModule *m) { ); } +// NOTE: gdb reads a `wchar_t` as UTF-32 on Linux, but a 16-bit UTF character as UTF-16; CodeView knows `wchar_t` as UTF-16 +gb_internal LLVMMetadataRef lb_debug_char16_type(lbModule *m) { + if (build_context.metrics.os == TargetOs_windows) { + return lb_debug_type_basic_type(m, str_lit("wchar_t"), 16, LLVMDWARFTypeEncoding_Unsigned); + } + return lb_debug_type_basic_type(m, str_lit("char16_t"), 16, LLVMDWARFTypeEncoding_Utf); +} + gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { i64 size = type_size_of(type); // Check size gb_unused(size); @@ -1013,8 +1021,8 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { case Basic_rawptr: { - LLVMMetadataRef void_type = lb_debug_type_basic_type(m, str_lit("void"), 8, LLVMDWARFTypeEncoding_Unsigned); - return LLVMDIBuilderCreatePointerType(m->debug_builder, void_type, ptr_bits, ptr_bits, LLVMDWARFTypeEncoding_Address, "rawptr", 6); + // NOTE: a pointer to no type is `void *`, as clang emits it, rather than a pointer to a byte shown as a C string + return LLVMDIBuilderCreatePointerType(m->debug_builder, nullptr, ptr_bits, ptr_bits, 0, "rawptr", 6); } case Basic_string: { @@ -1034,8 +1042,8 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { case Basic_string16: { // NOTE(bill): size_of(^u16) <= size_of(int) - // The data is `^wchar_t`, as `cstring16` is, so that debuggers show it as text - LLVMMetadataRef char_type = lb_debug_type_basic_type(m, str_lit("wchar_t"), 16, LLVMDWARFTypeEncoding_Unsigned); + // The data is a pointer to a UTF-16 character, as `cstring16` is, so that debuggers show it as text + LLVMMetadataRef char_type = lb_debug_char16_type(m); LLVMMetadataRef file = lb_get_file_metadata(m, m->info->runtime_package->files[0]); LLVMMetadataRef elements[2] = {}; @@ -1047,8 +1055,8 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { } case Basic_cstring16: { - LLVMMetadataRef char_type = lb_debug_type_basic_type(m, str_lit("wchar_t"), 16, LLVMDWARFTypeEncoding_Unsigned); - return LLVMDIBuilderCreatePointerType(m->debug_builder, char_type, ptr_bits, ptr_bits, 0, "cstring16", 7); + LLVMMetadataRef char_type = lb_debug_char16_type(m); + return LLVMDIBuilderCreatePointerType(m->debug_builder, char_type, ptr_bits, ptr_bits, 0, "cstring16", 9); } case Basic_any: @@ -1294,6 +1302,22 @@ gb_internal LLVMMetadataRef lb_get_base_scope_metadata(lbModule *m, Scope *scope } } +// NOTE: gdb looks a declared enum up by name and stops at the first declaration it finds rather than the definition, +// and it cannot look up a declared type whose name starts with `#`, so on DWARF these are defined in every module +// which uses them, as clang does with enums +gb_internal bool lb_debug_type_is_defined_everywhere(Type *bt) { + if (build_context.metrics.os == TargetOs_windows) { + return false; + } + switch (bt->kind) { + case Type_Enum: return true; + case Type_BitSet: return lb_debug_bit_set_is_flag_enum(bt); + case Type_Struct: return bt->Struct.soa_kind != StructSoa_None; + case Type_Basic: return bt->Basic.kind == Basic_typeid; + } + return false; +} + gb_internal LLVMMetadataRef lb_debug_type(lbModule *m, Type *type) { GB_ASSERT(type != nullptr); @@ -1352,7 +1376,7 @@ gb_internal LLVMMetadataRef lb_debug_type(lbModule *m, Type *type) { Array const &types_modules = m->gen->debug_types_modules; String record_name = {}; lbModule *owner = nullptr; - if (is_record && types_modules.count != 0 && record_bt->kind != Type_Tuple) { + if (is_record && types_modules.count != 0 && record_bt->kind != Type_Tuple && !lb_debug_type_is_defined_everywhere(record_bt)) { record_name = type_to_canonical_string(temporary_allocator(), type); owner = types_modules[string_hash(record_name) % types_modules.count]; }