Rust 编译器调试信息全解析:从 MIR 到调试器的完整技术栈(rustc dev-guide debuginfo 深度指南)
2026/9/12 0:24:22 网站建设 项目流程

Rust 编译器调试信息全解析:从 MIR 到调试器的完整技术栈(rustc dev-guide debuginfo 深度指南)

【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust

本篇文章以 rustc 开发指南(rustc-dev-guide)中的 debuginfo 章节为核心脉络,系统讲解 Rust 编译器如何生成调试信息、DWARF 与 PDB/CodeView 两大格式的差异、GDB/LLDB/CDB 三大调试器的能力边界,并结合本仓库源码深入剖析类型信息生成、可视化脚本与测试体系。读完本文,你将掌握调试信息从 rustc 的 MIR 一路流向调试器前端的完整调用链,理解Vec<T>等类型为何能以"合成子节点"的形式呈现,以及如何为 Rust 编写高效的调试器可视化脚本与调试信息测试。

什么是调试信息(Debug Info)

调试信息是编译器生成的一组附加数据,它让调试器能够在程序运行时正确还原程序状态。具体包括两大类内容:

  • 源码映射:把机器指令地址映射回源文件中的具体代码行,支撑断点、单步执行与源码级定位;
  • 类型布局信息:描述内存中的字节应该如何被解读和展示,例如某个地址处存储的是一个struct、一个枚举判别值还是一个指针。

调试信息是一个"过载"的术语,它实际上覆盖了从 Rust MIR 到用户在调试器屏幕上看到输出的每一层。完整的链路如下:

  1. rustc 检查 MIR,把相关的源码、符号和类型信息传递给 LLVM;
  2. LLVM 在编译期间将其翻译成目标平台特有的调试信息格式;
  3. 调试器读取并解释调试信息,映射源码行,并按照正确的布局定位和读取被调试程序(debugee)内存中的变量;
  4. 内置的调试器格式化与样式作用于变量;
  5. 用户自定义脚本进一步格式化变量;
  6. 调试器前端把变量展示给用户,中间可能经过额外的 API 层,例如 VSCode 扩展通过 Debug Adapter Protocol(DAP)与调试器通信。

说明:rustc-dev-guide 中这一小节刻意写得比必要更详细,目的是把分散在各处的信息汇集到一起,让读者对整个调试技术栈建立牢固认知。如果只想动手改可视化脚本,阅读 debugger-visualizers.md 与 testing.md 即可;如果需要修改 Rust 的调试节点生成逻辑,则看 rust-codegen.md。

两大调试信息格式:DWARF 与 PDB/CodeView

DWARF(*-gnu目标的主格式)

DWARF 是*-gnu类目标(Linux、macOS、BSD 等)的主要调试信息格式,通常随二进制一起打包,但也可以借助 DebugFission 机制生成为独立文件。标准文本由 DWARF 委员会维护。

在工具链层面,可以用 gimli 目录中。

PDB / CodeView(*-msvc目标的主格式)

PDB 是微软创建的专有容器格式,用于*-msvc目标。需要注意"PDB"一词有多个含义——我们讨论的是普通 PDB 文件(Portable PDB 主要用于 .NET 应用,不在本文范围)。PDB 文件与编译产物分离,扩展名为.pdb

PDB 文件内部包含CodeView 对象,相当于 DWARF 中的 tag。CodeView 最初是 1985 年发布的调试器,原始定位是 C 语言调试,后来扩展支持 Visual C++。虽然格式至今仍有小幅演化以适配现代架构与语言,但很多改动没有文档或使用稀疏。

理解这段历史对处理 CodeView 对象至关重要:

  • 由于出身于 C,CodeView 对象的"特性集"非常有限,围绕 C 语言核心特性展开,缺少现代 DWARF 标准的许多便捷特性;
  • rustc 的调试信息栈中因此存在相当数量的 workaround,用来补偿 CodeView 的短板(下文"类型信息"一节会看到大量例子);
  • 由于格式专有,PDB/CodeView 的公开资料稀少、年代跨度大且相互矛盾,本文整理的主要公开资料源包括:TIS PE 规范(含长篇的 "Microsoft Symbol and Type Information" 章节,1993 年成文但至今细节准确)、LLVM 的 CodeView Overview 与 PDB Overview 文档、微软的microsoft-pdb(C/C++ 实现的 PDB 读取器,虽未覆盖完整规范但足以支撑其他 PDB 消费者)、pdb-rs(Rust 编写的 PDB 读写库,附带pdbtool可转储 PDB 文件,安装命令为cargo install --locked pdbtool)、Debug Interface Access SDK(DIA)以及社区整理的 PDB-Documentation 资料库。

三大调试器:GDB、LLDB 与 CDB

Rust 官方支持三个主流调试器,每个都有各自的需求、限制与怪癖,这给编译器与脚本维护带来了很大的兼容面:

调试器调试信息格式原生 Rust 支持表达式风格可视化脚本
GDBDWARF完整RustPython
LLDBDWARF 和 PDB部分C/C++Python
CDBPDBC/C++Natvis

注意:CDB 是微软的专有调试器,其底层引擎同样驱动着 WinDbg、KD、VSCode 的 Microsoft C/C++ 扩展以及 Visual Studio 调试器的一部分,文档中统一称之为 CDB。CDB 可以假定只在 Windows 上运行,而 GDB/LLDB 则无法对操作系统做任何假设。

虽然 GDB 与 LLDB 确实提供了原生支持 Rust 值布局的能力,但这并非必需——Rust 目前输出的调试信息与 C++ 非常相似,没有 Rust 支持的调试器也能以略微降级的体验工作。

尚未官方支持的调试器

文档特别提及两个有潜力的"未来选手":

  • Bugstalker:用 Rust 编写的 x86-64 Linux 调试器,专为调试 Rust 程序而生,前景可观但仍处于早期开发阶段;
  • RAD Debugger:Epic Games 出品的仅 Windows 的 GUI 调试器,拥有自定义调试信息格式(PDB 会被翻译成该格式),项目还附带一个能在链接阶段生成新调试信息格式的链接器。

源码级深挖(一):rustc 如何生成调试信息

调试信息生成的第一阶段要求 rustc 检查程序的 MIR 并将其传递给 LLVM,主要工作位于 compiler/rustc_codegen_llvm/src/debuginfo(部分类型名处理在 compiler/rustc_codegen_ssa/src/debuginfo)。rustc 通过DIBuilderAPI 与 LLVM 通信,这是 compiler/rustc_llvm 中对 LLVM 内部机制的薄封装。

类型信息的本质目标

类型信息通常包括:类型名、大小、对齐方式,以及(如相关)字段、泛型参数和存储修饰符。大部分工作发生在 compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs(枚举节点另有 metadata/enums 子目录)。

一个必须牢记的核心原则:目标不是"精确还原 Rust 中的类型表示",而是"以能让调试器最准确重建数据的方式表示它们"。这一区分是理解该层工作的钥匙——这一层的很多改动都是为了绕开调试器限制而做的 workaround。

假装自己是 C/C++:rustc 的调试信息怪癖

Rust 生成的 DI 节点在 CDB 与 LLDB 面前"假装"是 C/C++,这会带来一些反直觉、非惯用的调试信息。

指针与引用

  • 宽指针/宽引用/Box被当作含有data_ptrlength两个字段的 struct 处理;
  • 所有非宽指针、引用和Box指针都输出为指针节点,且不区分mut与不可变。曾有多轮尝试修复这一点,但始终没有简单的解决方案——直接使用各格式的referenceDI 节点存在隐患,因为 C++ 引用与 Rust 引用之间存在不可调和的语义差异(C++ 引用"不必然是对象、不一定占用存储",且没有引用数组、指向引用的指针、引用的引用)。目前提出的方案是直接对指针节点做 typedef。

const限定符表示不可变引用也有隐患:LLDB 内部会对变量的子值(struct 字段、数组元素)做缓存,并用启发式判断哪些值可安全缓存,const正是该启发式的一部分,而它与 Rust 内部可变性(interior mutability)构造的交互尚无研究。

DWARF vs PDB 的分流:虽然大部分类型信息比较直接,但目标调试信息格式是一个显著差异点——两种格式语义与限制不同,某些情况下需要生成略有区别的调试信息,由cpp_like_debuginfo相关调用门控。

MSVC 命名转换表

由于 MSVC 表达式解析器的限制,Rust 在为 PDB 生成调试信息时做了如下名称变换:

Rust 名称MSVC 名称
&str/&mut strref$<str$>/ref_mut$<str$>
&[T]/&mut [T]ref$<slice$<T> >/ref_mut$<slice$<T> >
[T; N]array$<T, N>
RustEnumenum2$<RustEnum>
(T1, T2)tuple$<T1, T2>
*const Tptr_const$<T>
*mut Tptr_mut$<T>
usizesize_t
isizeptrdiff_t
uNunsigned __intN
iN__intN
f32float
f64double
f128fp128

其中两个细节值得注意:一是 MSVC 表达式解析器会把>>当成右移,所以连续>之间必须用空格分隔(如ref$<slice$<T> >);二是诸如usizef32这类名字虽然会作为调试信息节点(外包一层 typedef 节点并带上 Rust 名)生成,但一旦 LLVM-IR 节点被转换为 CodeView 节点,这些类型名信息就会丢失——因为 CodeView 对原始类型有专门的简写节点,而简写节点没有"name"字段。

泛型与类型别名

  • Rust 会输出泛型类型信息(ArrayVec<T, N: usize>中的T),但不输出泛型值信息(其中的N)。
  • CodeView 没有针对泛型/C++ 模板的叶子节点,因此生成 PDB 调试信息时所有泛型信息都会丢失。有一些 workaround 允许调试器通过类型名反推泛型实参,但那是相当脆弱的方案;官方正在尝试联系微软修正这一缺陷,或使用某个未使用的 CodeView 节点类型作为替代。
  • Rust 在若干场景会输出 typedef 节点以弥补调试器限制,但目前不为源码中的类型别名输出节点。

枚举(Enum)的两种表示

枚举 DI 节点生成于 compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums。

DWARF 侧:DWARF 有专门用于判别联合(discriminated union)的节点DW_TAG_variant。它是一个容器,引用可能含或不含判别值的DW_TAG_variant_part节点,层级如下:

DW_TAG_structure_type (coroutine 的顶层类型) DW_TAG_variant_part (变体部分) DW_AT_discr (对判别 DW_TAG_member 的引用) DW_TAG_member (判别成员) DW_TAG_variant (变体 1) DW_TAG_variant (变体 2) DW_TAG_variant (变体 3) DW_TAG_structure_type (变体 1 的类型) DW_TAG_structure_type (变体 2 的类型) DW_TAG_structure_type (变体 3 的类型)

PDB 侧:PDB 没有专用节点,于是生成 C 风格的判别联合等价物:

union enum2$<RUST_ENUM_NAME> { enum VariantNames { First, Second }; struct Variant0 { struct First { // fields }; static const enum2$<RUST_ENUM_NAME>::VariantNames NAME; static const unsigned long DISCR_EXACT; enum2$<RUST_ENUM_NAME>::Variant0::First value; }; struct Variant1 { struct Second { // fields }; static enum2$<RUST_ENUM_NAME>::VariantNames NAME; static unsigned long DISCR_EXACT; enum2$<RUST_ENUM_NAME>::Variant1::Second value; }; enum2$<RUST_ENUM_NAME>::Variant0 variant0; enum2$<RUST_ENUM_NAME>::Variant1 variant1; unsigned long tag; }

一个重要细节:由于 LLDB 的限制,生成的DISCR_*值永远是u64(即便该枚举并非#[repr(u64)])。对 LLDB 来说这基本不是问题,因为DISCR_*值和tag无论其真实类型如何都会被读入uint64_t

源码级深挖(二):LLVM 层的翻译

当 Rust 调用 LLVM 的DIBuilder函数时,LLVM 会把给定信息翻译成与格式无关的"debug record"(可在 LLVM-IR 中直接查看)。

关键点在于:debug record 中的 tag始终以 DWARF tag 存储。如果目标平台需要 PDB 调试信息,codegen 阶段会把 debug record 交给一个模块(LLVM 项目中的CodeViewDebug.cpp),把 DWARF tag 翻译成对应的 CodeView 表示。这意味着 DWARF 是 rustc 与 LLVM 之间的"内部通用语言",PDB 支持是后置转换层。

源码级深挖(三):调试器内部

调试器负责把调试信息转换为内存中的表示。无论是调试信息的解释方式还是内存表示本身都是任意的——只要能在程序运行时重建有意义的信息即可。从原始调试信息到可用类型的流水线可能相当复杂。

GDB

GDB 的 Rust 支持位于其源码树的gdb/rust-lang.hgdb/rust-lang.c,表达式解析支持在gdb/rust-exp.hgdb/rust-parse.c

LLDB

LLDB 的调试信息处理依赖一组可扩展接口,主要定义在lldb/source/Plugins下,目的是允许第三方编译器开发者添加可在运行时由 LLDB 加载的语言支持。典型语言支持以插件流水线形式实现:*ASTParserTypeSystemExpressionParser/Language。已有的实现包括:Apple 的 Swift 支持分支、CodeLLDB 前分支的 Rust 支持、正在进行的 Rust 支持重实现,以及一个先于TypeSystemAPI 编写的 Rust 表达式解析器插件。

Rust 与 TypeSystemClang:LLDB 对 Rust 是"部分支持"——Rust 走的是为 C/C++ 构建的插件流水线(内含少量对 Rust 枚举类型的辅助),直接依赖 clang 编译器的类型表示。这给修改 LLDB 输出施加了严格限制:Rust 的需求相比"保证 C/C++ 编译与调试正确"始终是次要的。LLDB 官方对添加TypeSystemRust持开放态度,但那是一项巨大的工程。

DWARF 与 PDB 双轨:LLDB 是唯一能同时处理 DWARF 与 PDB 的调试器。PDB 支持曾分为dia(依赖 Visual Studio 分发的msdia140.dll)与native(基于公开资料从零实现)两种读取器;dia在 LLDB 21 及以前是默认,LLDB 22 起默认切到native,并计划彻底移除dianative可通过plugin.symbol-file.pdb.reader设置或环境变量LLDB_USE_NATIVE_PDB_READER=0/1切换。

调试节点解析:原始调试节点首先由DWARFASTParserPdbAstBuilder解析成更方便的格式(对 clang 而言是clang::QualTypeclang::Declclang::DeclContext)。翻译完成后,指向这些对象的指针被类型擦除为void*,再包装进CompilerTypeCompilerDeclCompilerDeclContext,关联到所属的TypeSystem。用 Rust 视角看大致是:

struct CompilerType { inner_type: *mut c_void, type_system: Arc<dyn TypeSystem>, } impl CompilerType { pub fn get_byte_size(&self) -> usize { self.type_system.get_byte_size(self.lang_type) } } impl TypeSystem for TypeSystemLang { pub fn get_byte_size(lang_type: *mut c_void) -> usize { let lang_type = lang_type as *mut LangType; // 操作 LangType 内部以确定其大小 ... } }

TypeSystem接口有三大职责:作为某门语言类型的"唯一权威"(可加入 LLDB 的类型系统池、检索SymbolFile、合成调试信息中不存在的类型);管理LangType/Decl/DeclContext对象生命周期;定制这些类型的"默认"外观与交互方式。其中GetIndexOfChildWithNameGetNumChildren格外重要:它们作用于类型而非值,返回值决定了 struct 的哪些部分能被 LLDB 的其余部分交互——如果某个字段被省略,它对 LLDB 而言就"不存在"了。

可视化脚本:让Vec<T>可读可点

"Visualizer"(可视化器)其实是个误称——真正目标不只是美化输出,而是提供对用户尽可能有用的交互界面。可视化器接口允许生成"合成子节点":调试信息中不存在、但可以从语言与类型本身的不变量推导出的字段。最经典的例子:让用户直接与Vec<T>的元素交互,而不是面对裸的*mut u8堆指针、长度和容量。

随工具链分发的支持脚本

rust-lldbrust-gdbrust-windbg.cmd随 Rust 工具链分发(本仓库中位于 src/etc 下)。它们负责定位相应的调试器与工具链的可视化脚本,然后在被调试程序启动/附加之前,用合适的参数启动调试器并加载脚本。

#![debugger_visualizer]属性

该属性允许 Rust 库作者把类型的 pretty printer直接内嵌进库本身的编译产物中,调试器自动加载这些脚本,用户获得无缝体验。目前该属性对 GDB 与 natvis 脚本生效:

  • GDB 的 Python 脚本嵌入二进制的.debug_gdb_scripts段,由 compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs 完成;
  • Natvis 文件可通过/NATVIS链接器选项嵌入 PDB,且在类型解析可视化器时拥有最高优先级;属性指定的文件被收集进CrateInfo::natvis_debugger_visualizers,再作为链接器参数加入;
  • LLDB 目前不支持该属性,未来可能的方案包括官方的 formatter bytecode(避免内嵌完整 Python 脚本的安全顾虑,但需要实现某种 DSL/小型编译器),或复制 GDB 策略、在二进制中自建段并利用 LLDB Python API 读取原始段后手动加载。

LLDB 的三种定制机制

LLDB 提供三种输出定制机制:Formats(设置原始类型的默认打印格式,Rust 几乎总是需要把unsigned charsigned charcharu8i8覆盖为十进制);Synthetic providers(Python 类,包装SBValue并接管子节点访问);Summary providers(Python 函数,返回直接展示给用户的字符串)。一个关键的 LLDB 怪癖:Python 脚本经由command script import <path>.py注入后,用type synthetic add/type summary add挂载到某个 category(Rust 官方使用名为Rust的 category);脚本还可以实现__lldb_init_module(debugger, ...)在导入末尾、控制权交还 CLI 之前初始化状态并注册 providers。

Synthetic provider 的标准接口大致如下(get_child_indexget_child_at_index为必需,其余可选):

class SyntheticProvider: def __init__(self, valobj: SBValue, _lldb_internal): ... def update(self) -> bool: ... # 可选 def has_children(self) -> bool: ... # 可选 def num_children(self, max_children: int) -> int: ... # 可选 def get_child_index(self, name: str) -> int: ... def get_child_at_index(self, index: int) -> SBValue: ... def get_type_name(self) -> str: ... # 可选 def get_value(self) -> SBValue: ... # 可选

update()的返回值直接控制 LLDB 的子节点缓存:返回True表示子节点数量与地址未变、可复用缓存(在错误场景返回True会导致调试器输出错误信息);返回False表示有变化、缓存被冲刷后从零重建,不确定时这是更安全的选择。缓存本质上是"指向内存的指针":例如 slice 的data_ptrlength未变时返回True是合适的,即使 slice 是可变且元素被改写(如slice[0] = 15),缓存指针依然能看到新数据;反之data_ptr变了就必须冲刷缓存。

一个完整例子:Vec<T>的 SyntheticProvider

Rust 官方实现位于 src/etc/lldb_providers.py。核心思路:Vec的堆指针是*mut u8而非*mut T,所以 provider 要额外保存元素类型;data_ptr、长度、容量可能变化,因此在__init__中默认初始化:

class VecSyntheticProvider: valobj: SBValue data_ptr: SBValue len: int cap: int element_type: SBType __slots__ = ("valobj", "data_ptr", "len", "cap", "element_type", "__weakref__") def __init__(valobj: SBValue, _dict) -> None: self.valobj = valobj self.element_type = SBType() # invalid type 是比 None 更好的默认值 # 特殊处理以兼容 DWARF/PDB 差异 if (arg := valobj.GetType().GetTemplateArgumentType(0)): self.element_type = arg else: arg_name = next(get_template_args(valobj.GetTypeName())) self.element_type = resolve_msvc_template_arg(arg_name, valobj.GetTarget())

update()只需检查指针与长度是否变化(容量变化若引发重分配,data_ptr地址必然不同,因此容量可省略检查):

def update(self): ptr = self.valobj.GetChildMemberWithName("data_ptr") len = self.valobj.GetChildMemberWithName("length").GetValueAsUnsigned() if ( self.data_ptr.GetValueAsAddress() == ptr.GetValueAsAddress() and self.len == len ): return True # 子节点地址偏移与数量仍有效,可复用缓存 self.data_ptr = ptr self.len = len return False

为了让元素以[0][1]形式呈现、同时仍可访问长度与容量,get_child_indexlencap/capacity分配了u32::MAX - 1u32::MAX - 2的哨兵索引(几乎保证不与元素索引重叠);get_child_at_index则通过CreateValueFromAddressdata_ptr + index * element_type.GetByteSize()逐个构造元素子节点。挂载时用正则匹配所有Vec类型:

type synthetic add -l lldb_lookup.synthetic_lookup -x "^(alloc::([a-z_]+::)+)Vec<.+>$" --category Rust type summary add -F lldb_lookup.summary_lookup -x "^(alloc::([a-z_]+::)+)Vec<.+>$" --category Rust

启用 provider 前后的对比一目了然——没有 provider 时只能看到buf/ptr/cap/len的原始结构且vec_v[0]下标会报错;启用后:

(lldb) v vec_v (Vec<int>) vec_v = vec![10, 20, 30, 40, 50] { [0] = 10 [1] = 20 [2] = 30 [3] = 40 [4] = 50 } (lldb) v vec_v[0] (int) vec_v[0] = 10 (lldb) v vec_v.len (unsigned long long) vec_v.len = 5

可视化脚本的性能红线

可视化脚本处于性能敏感链路中:用户在可视化脚本上多花的每一毫秒,都会推迟看到输出。大型栈帧包含许多大容器类型时尤其痛苦——VSCode 之类的 GUI 会一次性请求整个栈帧,可能造成数十秒乃至数分钟的卡顿。由于没有编译器帮忙优化 Python 代码,文档给出了一系列实操建议:

  • 一切都会分配内存,连int也是;
  • 尽量用 tuple:list等价于Vec<Box<[Any]>>,tuple 等价于Box<[Any]>,少一层间接、不携带多余容量、不能伸缩,且 Python 会缓存并回收所有大小 ≤20 的 tuple 的底层分配;
  • 正则很慢,能用简单字符串操作就避免;
  • 字符串不可变,许多字符串操作隐式复制内容;拼接大字符串列表用"".join(...)通常最快,小型简单变换用 f-string 最快;
  • 函数调用有开销(哪怕空函数),热路径考虑手动内联;
  • 局部变量访问远快于全局与内建;.成员访问也慢,把深层嵌套值重赋给局部变量(如h = a.b.c.d.e.f.g.h);
  • 继承的方法/字段访问约慢 2 倍,尽量避开继承;
  • 尽可能用__slots__显著加速字段访问;
  • match / if..elif..else 不会被优化,条件按顺序逐条检查,可用字典分发或值表替代;
  • 尽量惰性计算;列表推导通常比循环快,生成器推导更省内存但稍慢(可类比 Rust 的iter.map():列表推导相当于末尾collect::<Vec<_>>,生成器推导不 collect)。

调试信息测试体系

调试信息测试回答三个问题:我们是否按预期输出信息?输出能否被调试器读取?可视化脚本是否按预期工作?第一个问题通常由 tests/codegen-llvm 回答;后两者由 tests/debuginfo 回答,由 compiletest 执行。当前测试套件正处于大规模重写中(见文档中的跟踪 issue),核心变化有两点:tests/debuginfo对 GDB/LLDB 改为opt-in;新增$DEBUGGER-repr指令。

repr指令

$DEBUGGER-repr命令会脱糖(desugar)为:

//@ $DEBUGGER-command:repr $VAR_NAME //@ $DEBUGGER-check:$VAR_NAME ok

测试框架拦截repr伪命令并运行特殊逻辑,与 tests/debuginfo 下<debugger>_input/<target_group>.json中存储的数据比对。"目标组"覆盖无法保证输出一致的目标集合(当前为non_windowswindows_gnuwindows_msvc,由 src/etc/lldb_batchmode/common.py 中的Target枚举定义),列表被刻意保持最短,因为每个目标都意味着一套需要在变更时同步更新的测试数据。

普通测试转repr很容易:把command+check替换成单行repr指令,然后带--bless运行(例如./x test tests/debuginfo/basic-types/main.rs --bless)。--bless会更新内存表示、据此测试,若无错误则写回目标文件(必要时新建)。若你拥有其他目标平台的机器,还需分别为各目标组 bless(例如 Windows 机器分别对x86_64-pc-windows-msvcx86_64-pc-windows-gnubless,再用 WSL 对x86_64-unknown-linux-gnubless)。

实现层面:TargetData(所有调试器共用同一 schema)通过dataclasses.asdict转字典、用 Python 内置 JSON 库序列化;类型信息在一次调试会话中唯一且不变,因此只在顶层存一次、其余按名引用;指针值每次运行都变,因此指针变量不存值(等价于-check指令中的通配[...]);BlessMetadataTargetData保存但不参与比对,仅用于记录测试数据的生成方式。错误不会立即终止测试(尤其--bless会更新全部数据,必须打印所有错误供读者判断),无错误的变量向 stdout 打印$VAR_NAME ok供 compiletest 匹配;退出前还会校验INPUT_DATA中所有类型与变量都已被检查过。

LLDB 版本迷思

Apple 随 Xcode 分发的 LLDB 分支(含 Swift 支持)不使用 LLVM LLDB 的版本号方案,且无法在两者间自动换算。排查问题时可通过 Swift LLVM 仓库中对应 release 分支的version.gni手动核对基础 LLVM 版本。例如lldb-1703.0.236.21(Apple Swift 6.2.3)大致对应 LLVM LLDB 19.1.5——而 LLDB 19 是首个支持 Type Recognizer 函数的版本,据此即可推断 CI 中该 LLDB 具备的能力。

延伸阅读

本文基于 src/doc/rustc-dev-guide/src/debuginfo 目录整理,该目录下每个子文档都值得按需深入:

  • rust-codegen.md——修改 Rust 调试节点生成必读;
  • debugger-visualizers.md——可视化脚本全貌与性能指南;
  • lldb-visualizers.md——LLDB Python provider 完整接口、Vec<T>完整示例与各类坑位;
  • llvm-codegen.md——LLVM debug record 与 DWARF→CodeView 翻译;
  • lldb-internals.md——LLDBTypeSystem插件架构;
  • gdb-internals.md——GDB Rust 支持位置;
  • testing.md——repr指令与--bless细节。

对应源码入口:rustc 侧调试信息生成见 compiler/rustc_codegen_llvm/src/debuginfo(含 metadata.rs、gdb.rs);测试基础设施见 src/etc/lldb_batchmode 与 src/etc/lldb_providers.py;支持脚本 rust-lldb、rust-gdb、rust-windbg.cmd 位于 src/etc。

【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询