指针不为空宽高也正常游戏里的图片却歪成了斜条纹。调试器能列出数字下一步能否把这些数字还原成你真正关心的图像RAD Debugger 的思路是先为复杂的调试信息建立一份共同的“说明书”。01一张坏掉的图片为什么会把人困在 Watch 窗口里设想你在开发一个小游戏。角色贴图从文件解码到 CPU 内存再上传给 GPU。最近换了一个图像库画面开始出现斜条纹颜色偶尔还不对。你打断点检查几个变量structImage{unsignedchar*pixels;intwidth;intheight;intstride_bytes;};pixels不是空指针宽高正确。展开内存里面也是一串合理的字节。问题在于一串字节是否合理与一张图片是否正确是两种判断。你真正想知道的是这段缓冲区按 RGBA 解释以后长什么样每一行是否从正确位置开始红蓝通道有没有交换上传前就坏了还是上传后才坏如果能在调试器里直接看这张图就能少做一轮导出、写文件、打开图片的工作。这也是理解 RAD Debugger 的一个合适入口让程序状态以接近业务问题的形态出现减少开发者在数字和现象之间来回翻译。02先纠正一个误会调试器并没有停在三十年前原稿用“调试器三十年没进化”制造冲突但作为技术结论太强。Visual Studio 的 Natvis 可以定制原生对象的显示官方文档还列出了用于查看内存位图的 Image Watch 扩展。[1] rr 则提供录制、确定性重放和反向调试。[2]所以不能说现代调试器普遍看不了图片也不能说过去发生过的执行永远无法再看。更准确的问题是这些能力如何成为连贯、低摩擦的调试体验开发一个新的调试工具又要先跨过多少格式与平台的门槛RAD Debugger 的辨识度在于它同时探索了调试信息格式、分层实现与图形交互。格式是重要一环进程控制、多线程、栈展开、优化代码和视图语义也都是难题。03调试信息机器指令旁边的一份导航地图编译以后CPU 执行的是机器指令。开发者看到的函数名、源码行、局部变量和类型并不会自动随指令保留下来。调试信息帮助工具建立它们之间的联系。可以把它理解成几个问题的索引开发者的问题调试信息需要提供什么当前停在哪一行指令位置与源码行的对应这个地址属于哪个函数函数范围与名称image.width如何读取类型、成员与位置描述局部变量在哪里当前指令位置下的寄存器或内存位置规则这份地图本身也很复杂。LLVM 的 PDB 文档说明了其多流组织及类型、符号等信息结构。[3] DWARF 则是另一套调试信息标准其位置表达式可以描述如何计算变量的位置或值。[4]PDB 与 DWARF 并不是各操作系统唯一合法的选择。更稳妥的说法是PDB 常见于 Windows 原生工具链DWARF 常见于 ELF 与 Unix 类工具链。还有一个容易忽略的点符号文件告诉调试器如何找变量运行时的值仍要从目标状态中取得。它不是一份持续更新的内存快照。04RDI先把不同地图整理成同一份结构RAD Debugger 使用自己的 RAD Debug Info即 RDI 格式。现有工具链产生的调试信息经过转换再交给主要调试信息消费路径。[5]这个思路与编译器使用中间表示有相似之处输入来源可以不同后续工作尽量面向同一套结构。PDB 调试信息PDB 转换器DWARF 调试信息DWARF 转换器RDI源码与符号查询类型与位置解释调试界面目标内存与寄存器这是概念关系图不表示所有界面都直接读取原始 RDI也不表示每一种输入格式已完整支持。读 RDI 格式定义可以看到源文件与行表、类型与成员、作用域、变量、函数以及位置字节码等分区。[6]这意味着它要统一的远不止“函数名列表”还包括让表达式求值和源码定位能够工作的结构。例如我们的Image类型需要有成员布局image变量需要有当前位置下的定位规则。两者配合调试器才能先找到对象再读出它的pixels和stride_bytes。05“转换一次”省下了什么又没有省下什么如果每个消费模块都直接理解 PDB 和 DWARF输入格式的差异就容易扩散到查询、类型分析与界面中。加入 RDI 后输入转换器承担格式差异消费方面向统一结构。这让新增输入格式时许多上层逻辑可以继续复用。所谓 N×M 向 NM 收敛更适合作为依赖关系的设计目标而非严格的工作量公式。因为转换本身仍然困难两种格式的语义不一定一一对应。某些语言、内联、位域和变量位置可能需要特殊处理。编译优化删掉的信息不能靠转换恢复。格式演进需要转换器与消费者共同维护。RDI 头文件包含格式标识与编码版本。[6] 对采用者来说它首先是项目维护的格式契约不能直接称为业界通用标准也不能默认永远向后兼容。这层抽象集中承担格式复杂度让复杂度不必在每个上层模块重复出现。06重新核对版本Linux 与 DWARF 已经不只是路线图截至本文核验Releases 页面标注的最新发布为v0.9.29-alpha发布于 2026 年 9 月 30 日。[7]这里有个重要的信息冲突根 README 开头仍保留“只支持本地 Windows x64PDB”的描述但该版本发布说明已经宣布初步原生 Linux x64 调试支持。[5][7]本文采用具体发布说明更新状态项目当前应怎样理解Windows x64项目已有的主要调试路径仍属 alphaLinux x64初步可试用需要自行构建稳定性更弱DWARF→RDI已有转换实现仍存在语义与类型处理边界Minidump新增初步加载支持Linux 发布说明列出了forkvfork尚未支持、TLS 定位依赖 libc 条件、特定栈展开与位域问题。[7]因此“已经支持 Linux”也需要带上“初步”二字。这条新进展之所以值得关注是统一调试信息路线开始跨越不同工具链。它也提醒我们读活跃项目时版本说明有时比 README 的总括句更新。07格式以外还需要一套不会把界面拖住的工作方式大型程序的调试信息可能很大。读文件、转换、搜索符号和反汇编如果都占着界面线程用户会感觉每一次操作都在等待。项目把这些职责分层当前 README 使用的名称也已与原稿有所变化。[5]当前层名负责的问题demon低层本机进程控制抽象ctrl/dbg_engine控制、单步与核心调试逻辑dbg_info异步调试信息转换、加载与查询disasm反汇编生成与缓存eval表达式分析、类型检查与求值eval_visualization求值结果的显示组织旧稿的dbgi、dasm、type_graph不应不加核对地当作当前目录导航。这里最值得学习的不是目录数量而是“慢工作如何与交互分开”。本文的架构推演是界面提出查询后先显示加载状态后台准备结果再依据当前目标状态刷新视图。等待中的旧结果还要避免被误用为新状态。缓存可以降低重复工作状态一致性决定缓存是否还能用。这是所有实时调试界面都会面对的设计问题。08Watch 窗口里的表达式其实也要经历一次小型编译输入image.pixels[20]看起来只是“读一个值”。调试器实际需要完成一连串工作识别名称与运算符、找到作用域里的变量、确定类型、计算位置最后读取目标数据。如果程序已经被优化同一变量在不同指令范围还可能处在不同位置甚至无法恢复原值。DWARF 标准的位置描述正是处理这类问题的一部分。[4]RAD 的eval层采用词法分析、解析、类型检查、IR 生成与求值的组织方式。[5]这让表达式能力可以独立于具体界面发展。对于我们的图片问题类型系统回答“pixels是指向什么的指针”表达式引擎回答“当前它指向哪里”可视化回答“这些字节应该怎样成为一张图”。三者不能互相替代。知道地址不等于知道像素格式知道指针类型也不等于知道图片尺寸。09让字节成为图片还缺一份业务解释回到开头的斜条纹。假设图片宽 64 像素每像素 4 字节那么有效像素占 256 字节。但图像库可能把一行存成 272 字节后面 16 字节是填充。正确读取第(x,y)个像素应使用[\text{address}(x,y)\text{base}y\times\text{stride_bytes}x\times4]如果错误地用width×4作为每行跨度第二行就开始读到上一行的填充后面的行会持续错位。这种错误在 Watch 窗口里很隐蔽在图像里可能一眼可见。下面是一个用于学习的缓冲区生成程序。它不创建图形窗口也不依赖 RAD 的专属 API#includestdio.henum{WIDTH64,HEIGHT32,STRIDEWIDTH*416};staticunsignedcharpixels[HEIGHT*STRIDE];intmain(void){for(inty0;yHEIGHT;y){for(intx0;xWIDTH;x){unsignedchar*ppixelsy*STRIDEx*4;p[0](unsignedchar)(x*4);/* R */p[1](unsignedchar)(y*8);/* G */p[2]80;/* B */p[3]255;/* A */}}puts(Set a breakpoint here and inspect pixels.);getchar();return0;}编译时保留调试信息并关闭优化在puts一行打断点。先查看pixels的字节再按当前发布包内的使用说明选择相应可视化方式。应当向查看器提供的语义是RGBA8、宽 64、高 32、每行 272 字节。如果所选位图规则只能解释紧密排列的图像就不能假设它支持这个步长可以先把STRIDE改为WIDTH*4验证基础显示再研究带填充的读取方式。本文不提供未经核验的 RAD 位图规则语法。这段代码演示的是可视化所需的元信息不保证当前任意查看器都能直接接收全部参数。还要划清 CPU 与 GPU 的边界这里观察的是 CPU 缓冲区。显示这块内存不等于已经读取 GPU 纹理也不等于可以单步 GPU shader。10保存项目、打开崩溃转储、重放执行是三种能力“昨天的调试会话今天还能打开”这句话至少有三种含义。能力留下了什么能否恢复完整执行历史项目与界面配置目标、断点、观察表达式等不等于保留过去的运行状态崩溃转储某一时刻记录的进程状态范围取决于转储内容不等于所有时刻都存在录制与重放支持重现执行所需的记录取决于工具与目标支持条件v0.9.29-alpha 将观察表达式与项目关联并加入初步 Minidump 加载。[7] rr 的官方说明则描述确定性重放与反向调试。[2]不要把这些功能混称为“时间旅行”。RDI 主要描述程序的调试元信息。单有它并没有昨天那次运行中每一刻的内存、输入与线程调度。这个区分也很适合评价未来功能你希望保存的是工作台还是错误发生时的证据两者需要不同的数据设计。11怎么开始Windows 体验Linux 验证Windows先用发布包少加构建变量从 官方 Releases 获取 Windows 发布包解压后阅读包内 README再启动raddbg.exe。[5]先选择一个自己的小型 x64 程序保留配套 PDB验证源码断点、变量和调用栈。不要第一轮就用复杂生产进程验证所有功能。上面的教学程序可在 x64 Native Tools Command Prompt 中编译cl /nologo /Zi /Od demo.c /Fe:demo.exe /link /DEBUG这条命令用于生成带调试信息的示例目标不是构建调试器。如果要构建 RAD Debugger 本身git clone https://github.com/EpicGames/raddebugger.git cd raddebugger build.bat release官方要求 MSVC Build Tools v152017或更新版本与 Windows SDK构建输出位于build。构建过程会运行metagen生成相关代码与表。[5]Linux现在可以试但需要接受早期边界以 UbuntuDebian 为例准备 Clang、LLVM 工具、pkg-config 与图形依赖后从源码构建[5][8]sudoaptinstallclang llvm pkg-config\libfreetype6-dev libx11-dev libxext-dev libxfixes-dev\libxrandr-dev libgl-dev libegl-devgitclone https://github.com/EpicGames/raddebugger.gitcdraddebugger ./build.sh需要可用图形会话不要把上述 X11 相关依赖理解成任意桌面环境或无头环境都已验证支持。可用clang -g -O0 demo.c -o demo构建教学目标再按构建输出与随附使用说明启动调试器。本文未执行这些安装与构建步骤。12读源码最值得沿着哪三条路线不必从巨大的界面代码开始。本文建议沿着三个问题阅读想理解的问题推荐入口怎样把调试信息整理成统一契约lib_rdi、lib_rdi_make、rdi_from_pdb、rdi_from_dwarf怎样从表达式走到目标状态eval与控制相关层怎样把结果组织成可交互视图eval_visualization、raddbg、ui、draw、render项目以层组织代码避免循环依赖以便隔离问题与裁剪构建部分lib_层设计为独立复用。[5]这种分层与类型本身形成的引用关系要分开层依赖要求无环不代表程序里的递归类型不能存在。它的自研立即模式 UI 和渲染抽象也可以从这条路线理解。但 UI 自研不是丰富可视化的唯一条件成熟框架同样可以提供扩展。自研换来控制权也意味着维护字体、输入、布局与平台后端的责任。13它最值得带走的是“说明书”与“现场”分开从本文的图片问题来看调试其实需要三份东西程序说明书符号、类型、源码与位置规则。执行现场目标内存、寄存器和当前线程状态。业务解释这段数据是图片、波形还是其他结构。RDI 提供了统一第一部分的路线控制与求值连接第二部分视图与规则组织第三部分。这是本文对项目设计的归纳。它比“换一种格式调试器就会全面进化”更接近实际工程。对做 IDE、开发者工具和大型 C 项目的人这套组织方式值得研究。对需要稳定调试复杂生产程序的人仍应按具体目标验证断点、栈展开、变量位置与故障诊断并保留成熟工具作为对照。最好的体验结果是你看到那张歪掉的图片以后马上意识到不是指针坏了是每行跨错了 16 个字节。调试器的价值就在于让程序已经知道的事实更快成为人能够理解的证据。参考资料与核验边界MicrosoftNatvis 与原生对象可视化已有可视化能力、Image Watch 扩展与视图规则。rr 官方网站录制、确定性重放与反向调试。LLVMThe PDB File FormatPDB 格式与多流组织。DWARF 5 标准调试信息、位置表达式与描述。RAD Debugger 官方仓库与 READMERDI、层化结构、构建方式与项目定位。其部分平台描述与发布说明存在更新不同步。RDI 格式定义 rdi.h格式标识、版本与主要分区。v0.9.29-alpha 发布说明Linux 初步支持、已知限制、Minidump 与项目观察表达式。Linux 构建脚本 build.sh编译器、图形依赖与 metagen 流程。本文没有做 RAD Debugger 运行验证或转换器性能复现。公式和程序为教学示例未将历史性能宣传、计划能力或版本中的初步支持写成完整兼容承诺。实际操作以所用发布包中的用户说明为准。