RAD调试器项目
**注意:**本README不包含调试器本身的使用说明和技巧,而是作为项目的技术概述。调试器的README,包括使用说明和技巧,可以在调试器发布包中找到,或者在本地构建后的build
文件夹中找到。
RAD调试器是一个原生的、用户模式的、多进程的图形化调试器。目前仅支持带有PDB的本地Windows x64调试,未来计划扩展和移植。我们未来还将支持原生Linux调试和DWARF调试信息。
RAD调试器目前处于ALPHA阶段。为了使调试器更加完善,如果您能在此提交发现的问题,并附上您能收集到的任何信息,如转储文件(以及您使用的构建版本)、重现步骤、测试可执行文件等,将会对我们有很大帮助。
您可以在这里下载调试器的预构建二进制文件。
RAD调试器项目旨在通过简化和统一底层调试信息格式来简化调试器。为此,我们构建了RAD调试信息(RDI)格式,这是调试器解析和使用的格式。为了与现有工具链兼容,我们会按需将PDB(未来还会包括嵌入DWARF的PE/ELF文件)转换为RDI格式。
RDI格式目前在代码中定义,位于src/lib_rdi_format
文件夹内的文件中。其他与该格式相关的重要文件夹包括:
lib_rdi_make
:用于创建RDI调试信息的"RAD调试信息制作"库。rdi_from_pdb
:我们的PDB到RDI转换器。可以作为辅助代码库层使用,也可以构建为带有命令行界面前端的可执行文件。rdi_from_dwarf
:我们正在开发的DWARF到RDI转换器。rdi_dump
:我们的RDI文本转储工具。
开发环境设置说明
注意:目前仅支持x64 Windows开发。
1. 安装所需工具(MSVC和Windows SDK)
要使用此代码库,您需要Microsoft C/C++ Build Tools v15 (2017)或更高版本,其中包含Windows SDK和MSVC编译器及链接器。
如果已安装Windows SDK(例如通过安装Microsoft C/C++ Build Tools),您也可以使用Clang进行构建。
2. 构建环境设置
可以在能够从命令行调用MSVC或Clang的终端中构建代码库。
通常通过调用vcvarsall.bat x64
来实现,该脚本包含在Microsoft C/C++ Build Tools中。这个脚本会自动被x64 Native Tools Command Prompt for VS <年份>
版本的普通cmd.exe
调用。如果您已安装构建工具,可以通过Windows开始菜单搜索"Native"轻松找到此命令提示符。
您可以通过运行以下命令确保MSVC编译器可从命令行访问:
cl
如果一切设置正确,您应该看到类似以下的输出:
Microsoft (R) C/C++ Optimizing Compiler Version 19.29.30151 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
usage: cl [ option... ] filename... [ /link linkoption... ]
3. 构建
在此终端中,cd
到代码库的根目录,然后运行build.bat
脚本:
build
您应该看到以下输出:
[debug mode]
[msvc compile]
[default mode, assuming `raddbg` build]
metagen_main.c
searching C:\devel\raddebugger/src... 299 files found
parsing metadesk... 12 metadesk files parsed
gathering tables... 37 tables found
generating layer code...
raddbg.cpp
如果一切正常,代码库的根目录中将会有一个build
文件夹,其中包含新构建的raddbg.exe
。
短期到中期路线图
初始Alpha测试阶段
项目的首要任务是确保最关键的调试器组件在本地、x64、Windows调试环境下能够极其可靠地运行。这包括调试信息转换、调试信息加载、进程控制、单步执行、求值(正确使用位置信息和类型信息),以及确保底层部分可用的强大前端。
我们认为调试器在所有这些方面都已经取得了长足的进步,但考虑到可能存在的语言、构建设置、工具链、使用的语言特性以及生成代码模式的巨大组合集,仍有一些情况尚未经过测试,因此仍存在一些问题。所以,我们认为首要任务是消除这些问题,以确保调试体验稳如磐石。
本地 x64 Linux 调试阶段
项目的下一个重点是将稳定的 x64 Windows 调试体验移植到支持本地 x64 Linux 调试。
调试器已经编写得能够抽象出在 Linux 或 Windows 上需要有所不同的部分,这主要是一项为这些抽象层构建不同后端的任务。
这个阶段的主要部分包括:
- 移植
src/demon
层以实现 Demon 本地进程控制抽象 API。 - 在
src/ctrl
层实现 x64 ELF Linux 展开器。 - 创建一个 DWARF 到 RDI 的转换器(类似于我们构建的 PDB 到 RDI 转换器)。
src/rdi_from_dwarf
中已有部分实现。 - 移植
src/render
层,以在兼容 Linux 的 API 上实现前端所需的所有渲染功能(Windows 上使用的后端是 D3D11)。 - 将
src/font_provider
层移植到兼容 Linux 的字体栅格化后端,如 FreeType(Windows 上使用的后端是 DirectWrite)。 - 将
src/os
层移植到 Linux。这包括核心操作系统抽象(虚拟内存分配、线程和同步原语等)和图形操作系统抽象(窗口、输入事件等)。
一旦上述列表完成,且每个部分都稳定可靠,我们努力创建的 Windows 调试体验也将在 Linux 机器上原生可用。
更远的未来!
在这两个主要阶段之后,我们可能会朝几个方向发展,比如远程调试、移植到不同架构、进一步改进调试器功能(如改进可视化引擎)等。但目前,我们主要集中在前两个阶段。
顶级目录描述
data
:构建时使用的小型二进制文件,用于嵌入构建产物或与之打包。src
:所有源代码。
设置代码库并构建后,还会存在以下目录:
build
:所有构建产物。不纳入版本控制。local
:本地文件,用于本地构建配置输入文件。
代码库简介
代码库按层组织。分层是为了隔离某些问题,并允许包含到各种构建中,而无需将代码库中的所有内容都拉入构建。层对应 src
目录内的文件夹。有时,src
目录内的一个文件夹会包含多个子层,但结构旨在保持相对扁平。
层大致与命名空间一一对应。这里的"命名空间"不是指特定的命名空间语言特性,而是指 C 风格命名空间的命名约定,在代码库中写为短前缀(通常 1-3 个字符),后跟下划线。使用这些命名空间是为了通过快速浏览代码就能理解某段代码属于哪一层。命名空间通常很短,以确保书写不会太麻烦。有时,多个子层会共享一个命名空间。少数层没有命名空间,但大多数有。命名空间根据使用上下文可能全大写或全小写。对于类型、枚举值和某些宏,它们是大写的。对于函数和全局变量,它们是小写的。
层之间相互依赖,但循环依赖会破坏层的可分离性和隔离效用(实际上形成一个大层),因此换句话说,层被安排成一个有向无环图。
一些层被构建成完全独立于代码库其他部分使用,作为其他代码库和项目中的库。因此,这些层不依赖于代码库中的任何其他层。包含这些层的文件夹以 lib_
为前缀,如 lib_rdi_format
。
以下是代码库中的层及其相关命名空间列表:
-
base
(无命名空间):通用的、贯穿整个代码库的构造。字符串、数学、内存分配器、辅助宏、命令行解析等。不依赖于其他代码库层。 -
codeview
(CV_
):用于解析和/或写入 CodeView 格式的代码。 -
coff
(COFF_
):用于解析和/或写入 COFF(通用对象文件格式)文件格式的代码。 -
ctrl
(CTRL_
):调试器的"控制系统"层。为所有附加进程实现异步进程控制、步进和断点。与附加进程同步运行。当它运行时,附加进程暂停。当附加进程运行时,它暂停。由另一个线程上的调试器前端驱动。 -
dasm
(DASM_
):异步反汇编解码器和缓存。用户请求进程中特定虚拟地址范围的反汇编,此层中实现的线程解码并缓存该范围的反汇编。 -
dbgi
(DI_
):异步调试信息加载器和缓存。加载存储在 RDI 格式中的调试信息。用户请求特定路径的调试信息,此层在单独的线程上加载相关的调试信息文件。如果 如有必要,它将启动一个单独的转换过程,将原始调试信息转换为RDI格式。 -
demon
(DEMON_
):一个用于本地机器、低级进程控制的抽象层。这个抽象用于为目标平台上的进程控制提供通用接口。用于实现ctrl
的部分功能。 -
df/core
(DF_
):调试器的非图形前端。实现了调试器的"实体缓存"(其中"实体"包括进程、线程、模块、断点、源文件、目标等)。实现了用于驱动进程控制的命令循环,用于实现步进命令和用户断点。实现了各种与实体相关数据的提取器和缓存,如完整的线程展开和局部变量映射。还实现了评估和评估可视化的核心构建块。 -
df/gfx
(DF_
):调试器的图形前端。基于df/core
提供所有图形功能,包括窗口、面板、各种调试器接口和评估可视化。 -
draw
(D_
):使用底层render
抽象层实现了一个高级图形绘制API,用于调试器的目的。为各种绘制命令提供高级API,同时处理批处理等问题。 -
eval
(EVAL_
):实现了一个表达式语言编译器,用于从调试器附加的进程和/或调试信息中评估变量、寄存器等。分为几个阶段,大致对应传统编译器的阶段 - 词法分析、语法分析、类型检查、IR生成和IR评估。 -
font_cache
(F_
):实现了栅格化字体数据的缓存,包括用于文本整形的CPU端数据和用于栅格化字形的GPU纹理图集。所有缓存信息都来自font_provider
抽象层。 -
font_provider
(FP_
):各种字体文件解码和字体栅格化后端的抽象层。 -
geo_cache
(GEO_
):实现了一个异步填充的GPU几何数据缓存,由hash_store
层缓存中的数据填充。用于异步准备调试器中内存可视化的数据。 -
hash_store
(HS_
):实现了一个通用数据块的缓存,以数据的128位哈希作为键。被其他层用作通用数据存储。 -
lib_raddbg_markup
(RADDBG_
):独立库,用于标记用户程序以配合raddbg
调试器的各种功能。不依赖于base
,可以独立地迁移到其他代码库。 -
lib_rdi_make
(RDIM_
):独立库,用于构建RDI调试信息数据。不依赖于base
,可以独立地迁移到其他代码库。 -
lib_rdi_format
(RDI_
):独立库,定义了核心RDI类型和用于读写RDI调试信息文件格式的辅助函数。不依赖于base
,可以独立地迁移到其他代码库。 -
metagen
(MG_
):一个元程序,主要用于生成代码和数据表。消费扩展名为.mdesk
的Metadesk文件,生成C代码,然后由手写的C代码包含。目前,它不分析代码库的手写C代码,但原则上这是可能的。这允许更容易且更少错误地管理大型数据表,这些数据表然后用于生成例如Cenum
和许多相关的数据表。还有一些其他生成功能,如将二进制文件或复杂的多行字符串嵌入源代码中。这一层不能直接依赖于代码库中的任何其他层,包括base
,因为它可能被用来为这些层生成代码。为了在metagen
程序中仍然使用base
和os
层功能,此层包含了一个单独的、重复的base
和os
版本。它们根据需要手动更新。这是为了确保元程序的稳定性。 -
msf
(MSF_
):用于解析和/或写入MSF文件格式的代码。 -
mule
(无命名空间):用于全面测试调试器功能的测试可执行文件。 -
natvis
(无命名空间):用于在其他调试器中可视化代码库类型的NatVis文件。 -
os/core
(OS_
):一个抽象层,提供来自操作系统的核心、非图形功能,通过一个抽象API,为每个目标操作系统实现。 -
os/gfx
(OS_
):一个建立在os/core
之上的抽象层,通过一个抽象API提供图形操作系统功能,为每个目标操作系统实现。 -
os/socket
(OS_
):一个建立在os/core
之上的抽象层,通过一个抽象API提供网络操作系统功能,为每个目标操作系统实现。 -
pdb
(PDB_
):用于解析和/或写入PDB文件格式的代码。 -
pe
(PE_
):用于解析和/或写入PE(可移植可执行文件)文件格式的代码。 -
raddbg
(无命名空间):将所有内容整合在一起的层,用于主图形调试器。没有太多"实质内容",只是驱动df
,实现命令行 选项等。 -
rdi_from_pdb
(P2R_
):我们实现的PDB到RDI转换。 -
rdi_from_dwarf
(D2R_
):我们正在进行的DWARF到RDI转换实现。 -
rdi_dump
(无命名空间):用于转储RDI调试信息文件文本化的转储实用程序。 -
regs
(REGS_
):支持架构上寄存器的类型、辅助函数和元数据。用于在demon
中读写寄存器,或查找寄存器元数据。 -
render
(R_
):提供抽象API的抽象层,用于通过各种GPU API在通用接口下进行渲染。不实现高级绘图API - 这层仅用于根据需要进行最小程度的抽象。更高级的绘图功能在draw
层实现。 -
scratch
(无命名空间):用于小型和临时测试或示例程序的暂存空间。 -
texture_cache
(TEX_
):实现异步填充的GPU纹理数据缓存,由hash_store
层缓存中的数据源填充。用于在调试器中异步准备内存可视化数据。 -
txti
(TXTI_
):用于异步加载、异步热重载、异步解析和异步修改源代码文件的机制。调试器用它来可视化源代码文件。用户请求文本行、标记和元数据,这些在后台线程上准备。 -
type_graph
(TG_
):用于分析和导航RDI调试信息文件中的类型结构的代码,具有构造调试信息中未找到的合成类型的附加功能。用于eval
和各种可视化功能。 -
ui
(UI_
):用于构建图形用户界面的机制。提供核心即时模式层次用户界面数据结构构建API,并有用于构建一些高级小部件的辅助层。