Merge pull request #7826 from bbodi/fix/debug-info-printers

Fix/debug info printers
This commit is contained in:
gingerBill authored and GitHub committed 2026-10-08 16:10:53 +01:00
commit 9b0b76d0f7
2 files changed
+78 -16

No files matched your search

+47 -9
View File
@@ -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)
+31 -7
View File
@@ -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<lbModule *> 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];
}