feat(debug): enhance type hinting in gdb and Python debug visualizers

This commit is contained in:
mynameisdeleted
2026-07-09 16:55:16 -04:00
parent 4f693f8cff
commit b93b7f204f
3 changed files with 136 additions and 34 deletions

View File

@@ -158,13 +158,19 @@ def _append_field(fields_out, name, value, is_pointer=False, type_hint=None):
def _process_member(label, member_val, node_id, fields_out, edges, nodes, visited, type_hint=None):
member_type = member_val.type.strip_typedefs()
# Every field gets a type hint, not just ones a wrapper (pretty-printer
# or Rust-enum-variant) has already labeled -- a plain `int`/`char *`
# member is just as worth showing as one that came out of an unwrapped
# std::optional. The caller's type_hint (a wrapper name) still wins when
# present, since that's more informative than the leaf's own raw type.
own_type_hint = type_hint or _type_name(member_type)
if member_type.code == gdb.TYPE_CODE_PTR:
try:
ptr_int = int(member_val)
except gdb.error:
ptr_int = 0
_append_field(fields_out, label, _addr_str(ptr_int), is_pointer=True, type_hint=type_hint)
_append_field(fields_out, label, _addr_str(ptr_int), is_pointer=True, type_hint=own_type_hint)
if ptr_int:
edges.append((node_id, _addr_str(ptr_int), label))
_visit_pointer(member_val, ptr_int, nodes, edges, visited)
@@ -199,7 +205,7 @@ def _process_member(label, member_val, node_id, fields_out, edges, nodes, visite
_append_field(fields_out, label, _format_scalar(member_val), type_hint=wrapper_name)
return
_append_field(fields_out, label, _format_scalar(member_val), type_hint=type_hint)
_append_field(fields_out, label, _format_scalar(member_val), type_hint=own_type_hint)
def _describe_struct(val, node_id, nodes, edges, visited):
@@ -217,7 +223,7 @@ def _describe_struct(val, node_id, nodes, edges, visited):
else:
for leaf_label, leaf_val in leaves:
_process_member(leaf_label, leaf_val, node_id, fields_out, edges, nodes, visited, wrapper_name)
nodes[node_id] = fields_out
nodes[node_id] = {"fields": fields_out, "type": wrapper_name}
return
t = val.type.strip_typedefs()
@@ -230,7 +236,7 @@ def _describe_struct(val, node_id, nodes, edges, visited):
continue
_process_member(f.name, member_val, node_id, fields_out, edges, nodes, visited)
nodes[node_id] = fields_out
nodes[node_id] = {"fields": fields_out, "type": _type_name(t)}
def _visit_pointer(ptr_val, ptr_int, nodes, edges, visited):
@@ -269,14 +275,16 @@ def _visit_pointer(ptr_val, ptr_int, nodes, edges, visited):
text = text[:500] + "…"
except (gdb.error, UnicodeDecodeError):
text = _format_scalar(ptr_val.dereference())
nodes[node_id] = [{"name": "value", "value": text, "isPointer": False}]
leaf_fields = []
_append_field(leaf_fields, "value", text, type_hint=_type_name(pointee_type))
nodes[node_id] = {"fields": leaf_fields, "type": _type_name(ptr_val.type.strip_typedefs())}
else:
if node_id in nodes:
return # already described (richly or otherwise) via another path
pointee = ptr_val.dereference()
nodes[node_id] = [
{"name": "value", "value": _format_scalar(pointee), "isPointer": False}
]
leaf_fields = []
_append_field(leaf_fields, "value", _format_scalar(pointee), type_hint=_type_name(pointee_type))
nodes[node_id] = {"fields": leaf_fields, "type": _type_name(ptr_val.type.strip_typedefs())}
def _walk_primary_root(root_val, nodes, edges, visited):
@@ -297,9 +305,9 @@ def _walk_primary_root(root_val, nodes, edges, visited):
_describe_struct(root_val, node_id, nodes, edges, visited)
return node_id
else:
nodes["root"] = [
{"name": "value", "value": _format_scalar(root_val), "isPointer": False}
]
leaf_fields = []
_append_field(leaf_fields, "value", _format_scalar(root_val), type_hint=_type_name(t))
nodes["root"] = {"fields": leaf_fields, "type": _type_name(t)}
return None
@@ -424,7 +432,12 @@ def build_graph_json(root_val, watched_roots):
continue
_add_named_root(sym.name, val, "global", nodes, edges, visited, roots, skip_addrs)
nodes_json = [{"id": nid, "fields": fields} for nid, fields in nodes.items()]
nodes_json = [
{"id": nid, "fields": node["fields"], "type": node["type"]}
if node.get("type")
else {"id": nid, "fields": node["fields"]}
for nid, node in nodes.items()
]
edges_json = [
{"from": src, "to": dst, "label": label}
for (src, dst, label) in edges