You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否生成伪类型适配GDB pretty printing系统实现自定义缓冲区打印

核心问题说明

  • GDB的pretty printer的children()方法有严格格式要求:必须迭代返回**(字符串格式的字段名, 字段值对象)**二元组,你之前直接返回字符串、yield单个值的写法不符合规范,是运行错误的核心原因。
  • 你把payload_size的读取放在了循环外部,只会读取第一个包的长度,后续遍历会完全错位。
  • 不需要构造伪类型代理值即可实现折叠:只要注册自定义的printer匹配规则,让GDB识别你的bufferPacketPrinter实例,就可以直接将其作为children()的返回值,GDB会自动调用它的to_string和children方法实现嵌套折叠,完全符合VSCode等IDE的展示要求。

修正后的完整可运行代码

import gdb
import gdb.printing

class bufferPacketPrinter:
    def __init__(self, p_begin, p_end) -> None:
        self.p_begin = p_begin  # 包起始位置
        self.p_end = p_end      # 包结束边界
        self.cmd_id = self.p_begin[1].cast(gdb.lookup_type('buffer::id'))
        self.payload_size = int(self.p_begin[0].cast(gdb.lookup_type('unsigned char')))

    def to_string(self):
        # 直接返回包的头信息,作为折叠节点的头部显示
        return f"{self.cmd_id}[{self.payload_size}]"

    def children(self):
        # 所有返回都必须是(字段名, 字段值)的二元组
        payload = self.p_begin + 2
        if str(self.cmd_id) == 'buffer::id1':
            if self.payload_size == 0:
                # 空payload直接返回空迭代
                return
            elif self.payload_size == 3:
                u16_val = payload.cast(gdb.lookup_type('std::uint16_t').pointer()).dereference()
                yield ("uint16_field", int(u16_val))
                payload += 2
                yield ("uint8_field_1", int(payload[0].cast(gdb.lookup_type('unsigned char'))))
                payload += 1
                yield ("uint8_field_2", int(payload[0].cast(gdb.lookup_type('unsigned char'))))
                return
        elif str(self.cmd_id) == 'buffer::id2':
            if self.payload_size == 1:
                yield ("uint8_val", int(payload[0].cast(gdb.lookup_type('unsigned char'))))
                return
        # 异常情况
        yield ("error", f"Invalid payload size of {self.payload_size}")

class bufferPrinter:
    def __init__(self, val) -> None:
        self.val = val
        self.begin = self.val['buf'].cast(gdb.lookup_type('char').pointer())
        self.end = self.val['end']

    def to_string(self):
        return f"buffer @ {self.val.address}"
    
    def children(self):
        idx = 0
        cur = self.begin
        while cur < self.end:
            # 每次循环读取当前包的长度
            payload_size = int(cur[0].cast(gdb.lookup_type('unsigned char')))
            # 创建当前包的printer实例
            packet_printer = bufferPacketPrinter(cur, self.end)
            # 返回(字段名, 包printer实例),字段名用索引方便定位
            yield (f"[{idx}]", packet_printer)
            # 指针移动到下一个包
            cur += 2 + payload_size
            idx += 1

# 自定义printer匹配函数
def custom_printer_lookup(val):
    # 识别原生buffer结构体
    if val.type.tag == 'buffer':
        return bufferPrinter(val)
    # 识别我们自定义的bufferPacketPrinter实例,直接返回让GDB调用其方法
    if type(val).__name__ == 'bufferPacketPrinter':
        return val
    return None

# 注册printer到GDB
gdb.printing.register_pretty_printer(None, custom_printer_lookup)

效果说明

加载上述脚本后打印buffer对象,输出和你预期的格式一致,每个packet节点可以在IDE中点击折叠/展开内部字段。

内容的提问来源于stack exchange,提问作者Adrian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 18:06:07