# TransPyC 调试器（vdb）设计文档

> **状态**：设计阶段，暂不实现  
> **创建时间**：2026-06-26  
> **目标**：为 TransPyC 编译的程序提供源码级调试能力，支持断点、变量查看、单步、调用栈、异常栈跟踪

---

## 1. 背景与动机

TransPyC 将 Python 子集编译为 LLVM IR 再编译为原生机器码。当前编译产物**完全不含调试信息**（无 DWARF/PDB），运行时崩溃只能看到错误码和消息，无法定位源码行、查看变量、回溯调用栈。

本设计文档规划一个 TransPyC 专用调试器 **vdb**，目标是：

- 源码级断点（`break main.py:30`）
- 变量查看（`print x` / `info locals`）
- 单步执行（`step` / `next`）
- 调用栈回溯（`backtrace`）
- 异常栈跟踪（`raise` 时记录栈帧）
- 硬错误捕获（segfault / 溢出）

---

## 2. 现状分析

### 2.1 已有可复用资产

| 资产 | 位置 | 复用方式 |
|------|------|---------|
| 变量映射三件套 `variables`/`_direct_values`/`_reg_values` | `lib/core/LLVMCG/BaseGen.py:79-81` | 序列化到 .tpd 供变量查看 |
| 变量类型信息 `var_type_info`/`var_signedness`/`var_struct_class` | `lib/core/LLVMCG/BaseGen.py:82-112,124` | 序列化为 VarBinding.TypeDesc |
| 源码行号跟踪 `_current_lineno`/`_set_node_info`/`_get_source_line` | `lib/core/LLVMCG/BaseGen.py:126-127,138,297-440` | 扩展为 SourceMap |
| 类元信息表 `class_members`/`class_member_defaults`/`class_parent`/`Vtables` | `lib/core/LLVMCG/BaseGen.py:85-100` | 序列化为 TypeMeta |
| 符号表序列化 `SymbolTable.ToDict` | `lib/core/SymbolTable.py:265-319` | vdb 直接加载，无需重建 |
| 异常机制 `__eh_msg_out__`/`__eh_code_out__` | `lib/core/Handles/HandlesRaise.py` / `HandlesTry.py` / `HandlesExprCall.py` | 扩展携带栈信息 |
| VLogger `compile_error(file, line, code)` 三元组 | `lib/core/VLogger.py:286` | 复用错误位置格式 |
| 本地堆指针记录 `_local_heap_ptrs`/`_var_to_heap_ptr` | `lib/core/LLVMCG/MemoryOps.py:7-15` | 堆对象追踪起点 |

### 2.2 关键缺口

| 能力 | 现状 | 缺口说明 |
|------|------|---------|
| DWARF 调试信息 | ❌ 完全未生成 | `lib/` 下无任何 `DIBuilder`/`!dbg`/`DICompileUnit` 调用 |
| PDB 调试信息 | ❌ 完全未生成 | Windows 下 WinDbg/VS 无法源码级调试 |
| 源码行 → IR 指令映射 | ❌ 无数据结构 | 最核心缺口，需从零构建 SourceMap |
| 变量名 → 运行时内存 | ⚠️ 仅编译期 ephemeral | `Gen.variables` 随 Gen 实例销毁，需持久化 |
| 类型元信息运行时查询 | ❌ 无 RTTI/反射 | `class_members` 等均编译期状态，未发射到二进制 |
| 运行时调用栈 | ❌ 完全无 | 异常仅传 `(code, msg)`，无栈帧链 |
| 硬错误处理 | ❌ 无 | `__builtin_trap` 仅声明未实现，segfault/SIGFPE 无捕获 |
| 断点/单步/变量查看 | ❌ 完全无 | `lib/` 下搜索 `breakpoint`/`step`/`watch` 零命中 |

---

## 3. 路线选择

### 3.1 方案 A：纯 DWARF + GDB/LLDB

**优点**：生态成熟，无需自研前端  
**缺点**：
- llvmlite 的 DIBuilder 支持度需验证
- Python 语义与 C 不完全对应（`self` 是指针但 Python 视为对象）
- SHA1 命名导致符号名难读
- 泛型特化类型（`Stack[int]`）在 DWARF 中表达困难
- 跨模块符号（`6d71a13705da41ad.main`）可读性差

### 3.2 方案 B：纯自研 vdb

**优点**：完全匹配 Python 语义，可显示原始源码，理解泛型/SHA1/跨模块  
**缺点**：开发量大，需自研调试协议与前端

### 3.3 方案 C：混合（推荐）

- 生成轻量 DWARF（仅行号 + 变量名）让 GDB 可用作后备
- 同时生成 TransPyC 专用调试元数据 `.tpd` 文件
- vdb 前端优先使用 `.tpd`，可降级到 GDB 后端

**本设计采用方案 C**，但优先实现 `.tpd` + vdb，DWARF 作为可选增强。

---

## 4. 核心数据结构

### 4.1 .tpd 文件格式（TransPyC Debug）

每个 `.ll` 文件配套生成一个 `.tpd` 文件（JSON 格式），包含以下结构：

```python
from dataclasses import dataclass, field
from typing import Any

@dataclass
class IrLocation:
    """单条 IR 指令的源码位置映射"""
    FuncName: str          # mangled 函数名（含 SHA1 头，如 '6d71a13705da41ad.main'）
    BlockName: str         # 基本块名（如 'entry'、'if.then'）
    InstrIdx: int          # 指令在块中的索引
    SourceFile: str        # 源码相对路径
    LineNo: int            # 源码行号
    ColNo: int             # 列号

@dataclass
class VarBinding:
    """变量绑定信息（作用域内）"""
    VarName: str           # Python 变量名（如 'x'、'self'）
    LlvmName: str          # alloca 名或 SSA（如 '%x'、'%".1"'）
    TypeDesc: str          # 类型描述（'i32' / 'Stack[int]*' / 'i8*'）
    IsSigned: bool         # 是否有符号整数
    StructClass: str | None  # 所属结构体类名（如 'Stack[int]'）
    StartLine: int         # 作用域起始行
    EndLine: int           # 作用域结束行
    Storage: str           # 'stack' | 'register' | 'direct' | 'global'
    StackOffset: int | None  # 栈偏移（仅 'stack'）

@dataclass
class TypeMeta:
    """类型元数据"""
    TypeName: str          # 'Stack[int]' / 'IntPair' / 'memory_block'
    Members: list[tuple[str, str, int]]  # (成员名, 类型, 字节偏移)
    Size: int              # 总大小（字节）
    Align: int             # 对齐
    Parent: str | None     # 父类名
    IsException: bool      # 是否异常类
    ExceptionCode: int     # 异常码（仅 IsException=True）
    HasVTable: bool        # 是否有虚表
    IsPacked: bool         # 是否紧凑布局

@dataclass
class FuncSig:
    """函数签名"""
    FuncName: str          # mangled 名
    RawName: str           # 原始 Python 名（去 SHA1）
    ReturnType: str        # 返回类型描述
    ParamNames: list[str]
    ParamTypes: list[str]
    IsExport: bool         # 是否导出（@t.CExport）
    IsMethod: bool         # 是否实例方法
    ClassName: str | None  # 所属类名（方法）
    SourceFile: str
    StartLine: int
    EndLine: int
    HasException: bool     # 是否可能抛异常（有 __eh_msg_out__）

@dataclass
class TpdFile:
    """完整的 .tpd 调试信息文件"""
    SourceMap: list[IrLocation]
    VarScopes: dict[str, list[VarBinding]]  # FuncName -> bindings
    TypeMeta: dict[str, TypeMeta]
    FuncSignatures: dict[str, FuncSig]
    ExceptionCodes: dict[int, str]          # code -> exception name
    SourceFiles: dict[str, list[str]]       # file -> 源码行列表（供 vdb 显示上下文）
    ModuleSha1: str | None
    Version: str = "1.0"
```

### 4.2 序列化格式

`.tpd` 文件使用 JSON，字段名采用 PascalCase（与代码一致）。示例：

```json
{
  "Version": "1.0",
  "ModuleSha1": "6d71a13705da41ad",
  "SourceFiles": {
    "App/main.py": ["import t", "import stdio", ...]
  },
  "SourceMap": [
    {"FuncName": "main", "BlockName": "entry", "InstrIdx": 0, "SourceFile": "App/main.py", "LineNo": 255, "ColNo": 0},
    ...
  ],
  "VarScopes": {
    "6d71a13705da41ad.test_generic_stack": [
      {"VarName": "s", "LlvmName": "%s", "TypeDesc": "Stack[int]*", "IsSigned": false, "StructClass": "Stack[int]", "StartLine": 66, "EndLine": 73, "Storage": "stack", "StackOffset": 0}
    ]
  },
  "TypeMeta": {
    "Stack[int]": {
      "TypeName": "Stack[int]",
      "Members": [["data", "[16 x i32]", 0], ["top", "i32", 64]],
      "Size": 68,
      "Align": 4,
      "Parent": null,
      "IsException": false,
      "ExceptionCode": 0,
      "HasVTable": false,
      "IsPacked": false
    }
  },
  "ExceptionCodes": {
    "1": "ValueError",
    "2": "TypeError",
    "99": "Exception",
    "100": "MyCustomError"
  }
}
```

---

## 5. 实现阶段

### 5.1 Phase 1：源码级断点（最小可用）

**目标**：`break main.py:30` + `continue` + `run`

#### 5.1.1 编译器改动

**位置**：`lib/core/LLVMCG/BaseGen.py`

1. **扩展 `_set_node_info`**：记录当前指令的源码位置到 `SourceMap`

```python
def _set_node_info(self, Node: ast.AST) -> None:
    self._current_lineno = getattr(Node, 'lineno', 0)
    self._current_col = getattr(Node, 'col_offset', 0)
    self._current_node_info = f" [line {self._current_lineno}, col {self._current_col}]"
    # 新增：记录到 SourceMap
    if self.func and self.builder and self.builder.block:
        loc: IrLocation = IrLocation(
            FuncName=self.func.name,
            BlockName=self.builder.block.name,
            InstrIdx=len(self.builder.block.instructions),
            SourceFile=self._current_source_file or '',
            LineNo=self._current_lineno,
            ColNo=self._current_col
        )
        self._source_map.append(loc)
```

2. **插入断点桩**：在每行源码对应的首条 IR 指令前插入

```llvm
; 运行时断点桩声明
declare void @__tpc_breakpoint(i32 %line, i8* %file)

; 在 main.py:255 的首条指令前插入
%src_file_ptr = getelementptr [12 x i8], [12 x i8]* @.src.main_py, i32 0, i32 0
call void @__tpc_breakpoint(i32 255, i8* %src_file_ptr)
```

3. **条件性插入**：仅当 `--debug` 编译选项启用时插入，release 构建完全剥离

4. **生成 .tpd 文件**：函数编译结束时，序列化 `SourceMap` 到 `.tpd`

#### 5.1.2 运行时库

新建 `includes/vdb_runtime.py`：

```python
import t
import c

# 全局断点表（行号 -> 是否启用）
_Breakpoints: t.CArray[t.CInt, 65536] | t.CPtr = [0]
# 当前调试器状态（0=运行, 1=暂停）
_DebugState: t.CInt = 0
# 当前源码文件名（用于断点匹配）
_CurrentFile: t.CArray[t.CInt8T, 256] | t.CPtr = [0]

@t.CExport
def __tpc_breakpoint(line: t.CInt, file: t.CPtr) -> None:
    """断点桩入口，由编译器在每个源码行前插入调用"""
    # 检查是否启用断点
    if _Breakpoints[line] == 0:
        return
    # 命中断点，陷入调试器循环
    _DebugState = 1
    _tpc_debug_loop(line, file)

def _tpc_debug_loop(line: t.CInt, file: t.CPtr) -> None:
    """调试器主循环，等待用户命令"""
    # 通过 IPC 或 stdio 与 vdb 前端通信
    # 解析命令：continue / step / next / print / info / backtrace
    while _DebugState == 1:
        cmd: t.CArray[t.CInt8T, 256] | t.CPtr = [0]
        stdio.scanf("%s", cmd)
        # ... 命令分派
```

#### 5.1.3 vdb 前端

新建 `lib/vdb/` 目录：

```
lib/vdb/
├── __init__.py
├── TpdLoader.py          # .tpd 文件加载器
├── BreakpointManager.py  # 断点管理
├── DebugSession.py       # 调试会话主循环
├── CommandParser.py      # 命令解析
└── SourceView.py         # 源码显示
```

**核心命令**：
- `break <file>:<line>` / `b <file>:<line>` — 设置断点
- `delete <n>` / `d <n>` — 删除断点
- `continue` / `c` — 继续执行
- `run` / `r` — 启动程序
- `list` / `l` — 显示当前源码上下文
- `info breakpoints` — 列出断点

### 5.2 Phase 2：变量查看

**目标**：`print x` / `info locals` / `info args`

#### 5.2.1 编译器改动

**位置**：`lib/core/LLVMCG/FuncGen.py` 或 `lib/core/Handles/HandlesFunctions.py`

函数编译结束时，序列化变量绑定：

```python
def _serialize_var_scope(self, FuncName: str) -> list[VarBinding]:
    bindings: list[VarBinding] = []
    for var_name, llvm_val in self.variables.items():
        if llvm_val is None:
            continue
        binding: VarBinding = VarBinding(
            VarName=var_name,
            LlvmName=llvm_val.name if hasattr(llvm_val, 'name') else '',
            TypeDesc=self._describe_llvm_type(llvm_val.type),
            IsSigned=self.var_signedness.get(var_name, True),
            StructClass=self.var_struct_class.get(var_name),
            StartLine=self._func_start_line,
            EndLine=self._func_end_line,
            Storage='stack',
            StackOffset=self._get_alloca_offset(llvm_val)  # 新增：记录栈偏移
        )
        bindings.append(binding)
    # 同样处理 _direct_values 和全局变量
    return bindings
```

#### 5.2.2 运行时变量读取

调试器通过以下方式读取变量值：

1. **栈变量**：通过 `StackOffset` + 当前栈帧基址读取
2. **全局变量**：通过符号表查找全局符号地址
3. **寄存器变量**：需调试器理解 SSA 值（较复杂，Phase 2 可跳过，仅支持 stack）

#### 5.2.3 类型渲染

```python
def RenderValue(llvm_val: bytes, type_meta: TypeMeta, type_desc: str) -> str:
    """根据类型渲染变量值"""
    if type_desc == 'i32':
        return str(int.from_bytes(llvm_val, 'little', signed=True))
    elif type_desc == 'i8*':
        # 尝试作为 C 字符串读取
        return f'"{read_cstring(llvm_val)}"'
    elif type_desc.endswith('*') and type_meta:
        # 结构体指针：展开成员
        return RenderStruct(llvm_val, type_meta)
    ...
```

### 5.3 Phase 3：单步执行

**目标**：`step` / `next` / `finish`

基于 SourceMap 实现：

- `step`（步入）：在下一行源码的首条 IR 指令前设临时断点，包括被调用函数内部
- `next`（步过）：在当前函数的下一行设临时断点，跳过函数调用
- `finish`（步出）：在当前函数的返回点设临时断点

**实现**：
1. 查 SourceMap 找到当前行的下一个源码行
2. 设置临时断点（一次性）
3. `continue`
4. 命中后自动删除临时断点

### 5.4 Phase 4：调用栈跟踪

**目标**：`backtrace` / `bt` / `frame <n>`

#### 5.4.1 编译器改动

每个函数入口/出口插入栈帧维护调用：

```llvm
; 函数入口
call void @__tpc_push_frame(
    i8* getelementptr([N x i8], [N x i8]* @.func.name, i32 0, i32 0),  ; 函数名
    i8* getelementptr([M x i8], [M x i8]* @.src.file, i32 0, i32 0),   ; 源文件
    i32 255                                                              ; 起始行
)

; 函数出口（每个 ret 前）
call void @__tpc_pop_frame()
```

#### 5.4.2 运行时栈结构

```python
import t

@t.CStruct
class FrameInfo:
    FuncName: t.CPtr        # i8* 函数名
    SrcFile: t.CPtr         # i8* 源文件
    LineNo: t.CInt          # 当前行号（动态更新）
    PrevFrame: 'FrameInfo' | t.CPtr  # 上一帧（链表）

# 全局栈顶
_FrameTop: FrameInfo | t.CPtr = t.CPtr(0)

@t.CExport
def __tpc_push_frame(func_name: t.CPtr, src_file: t.CPtr, line: t.CInt) -> None:
    frame: FrameInfo | t.CPtr = c.cast(FrameInfo, mb.alloc(FrameInfo.__sizeof__()))
    frame.FuncName = func_name
    frame.SrcFile = src_file
    frame.LineNo = line
    frame.PrevFrame = _FrameTop
    _FrameTop = frame

@t.CExport
def __tpc_pop_frame() -> None:
    if _FrameTop != t.CPtr(0):
        old: FrameInfo | t.CPtr = _FrameTop
        _FrameTop = _FrameTop.PrevFrame
        mb.free(old)
```

#### 5.4.3 断点桩更新当前行

`__tpc_breakpoint` 命中时，更新 `_FrameTop.LineNo`：

```python
@t.CExport
def __tpc_breakpoint(line: t.CInt, file: t.CPtr) -> None:
    if _Breakpoints[line] == 0:
        return
    if _FrameTop != t.CPtr(0):
        _FrameTop.LineNo = line
    _DebugState = 1
    _tpc_debug_loop(line, file)
```

### 5.5 Phase 5：异常栈跟踪

**目标**：`raise ValueError("msg")` 时输出完整调用栈

#### 5.5.1 编译器改动

**位置**：`lib/core/Handles/HandlesRaise.py`

在 `_HandleRaiseLlvm` 中，抛异常前捕获栈：

```python
def _HandleRaiseLlvm(self, Node: ast.Raise) -> None:
    # ... 原有错误码计算 ...
    # 新增：捕获调用栈
    capture_call = self.Gen.builder.call(
        self._get_or_declare_function('__tpc_capture_trace', ir.FunctionType(ir.VoidType(), [])),
        []
    )
    # ... 原有异常传播逻辑 ...
```

#### 5.5.2 异常结构扩展

将 `__eh_msg_out__`（i8**）扩展为指向包含栈的结构：

```python
import t

@t.CStruct
class ExceptionInfo:
    Code: t.CInt
    Message: t.CArray[t.CInt8T, 256] | t.CPtr
    TraceDepth: t.CInt
    TraceFuncs: t.CArray[t.CPtr, 64]    # 函数名指针数组
    TraceLines: t.CArray[t.CInt, 64]    # 对应行号
    TraceFiles: t.CArray[t.CPtr, 64]    # 对应源文件
```

#### 5.5.3 未捕获异常处理

在 `main` 函数返回前检查异常，打印栈：

```python
def main() -> t.CInt:
    # ... 用户代码 ...
    # 编译器自动插入：检查未捕获异常
    if _UnhandledException != t.CPtr(0):
        _tpc_print_trace(_UnhandledException)
        return 1
    return testcheck.end()
```

### 5.6 Phase 6：硬错误捕获

**目标**：segfault / 除零 / 栈溢出时进入调试器

#### 5.6.1 信号处理器注册

```python
import t
import c

@t.CExport
def __tpc_signal_handler(sig: t.CInt) -> None:
    """OS 信号处理入口"""
    __tpc_capture_trace()
    # 打印信号类型
    if sig == 11:  # SIGSEGV
        stdio.printf("Segmentation fault\n")
    elif sig == 8:  # SIGFPE
        stdio.printf("Arithmetic exception\n")
    # 陷入调试器
    _tpc_debug_loop(0, t.CPtr(0))

def __tpc_install_signal_handlers() -> None:
    """在 main 入口前自动调用"""
    c.signal(11, __tpc_signal_handler)  # SIGSEGV
    c.signal(8, __tpc_signal_handler)   # SIGFPE
```

#### 5.6.2 编译器改动

在 `main` 函数入口自动插入 `__tpc_install_signal_handlers()` 调用（仅 `--debug` 模式）。

---

## 6. vdb 命令参考

### 6.1 启动与控制

| 命令 | 缩写 | 说明 |
|------|------|------|
| `run [args]` | `r` | 启动被调试程序 |
| `continue` | `c` | 继续执行至下一断点 |
| `step` | `s` | 单步进入函数 |
| `next` | `n` | 单步跳过函数 |
| `finish` | `f` | 执行至当前函数返回 |
| `quit` | `q` | 退出调试器 |

### 6.2 断点管理

| 命令 | 缩写 | 说明 |
|------|------|------|
| `break <file>:<line>` | `b` | 设置源码行断点 |
| `break <func>` | `b` | 设置函数入口断点 |
| `tbreak <file>:<line>` | `tb` | 临时断点（命中后删除） |
| `delete <n>` | `d` | 删除断点 n |
| `info breakpoints` | `i b` | 列出所有断点 |

### 6.3 变量查看

| 命令 | 缩写 | 说明 |
|------|------|------|
| `print <expr>` | `p` | 打印表达式值 |
| `info locals` | `i lo` | 列出当前局部变量 |
| `info args` | `i ar` | 列出函数参数 |
| `display <expr>` | - | 每次暂停自动显示 |
| `set <var>=<val>` | - |修改变量值 |

### 6.4 栈操作

| 命令 | 缩写 | 说明 |
|------|------|------|
| `backtrace` | `bt` | 显示调用栈 |
| `frame <n>` | `fr` | 切换到第 n 帧 |
| `up` | - | 向上切换一帧 |
| `down` | `do` | 向下切换一帧 |

### 6.5 源码浏览

| 命令 | 缩写 | 说明 |
|------|------|------|
| `list` | `l` | 显示当前行上下文 |
| `list <file>:<line>` | `l` | 显示指定位置 |
| `info source` | `i so` | 显示当前源文件信息 |

### 6.6 类型与结构

| 命令 | 缩写 | 说明 |
|------|------|------|
| `ptype <typename>` | `pt` | 显示类型定义 |
| `info types` | `i ty` | 列出所有类型 |
| `info exceptions` | `i ex` | 列出异常码表 |

---

## 7. 实现优先级

| 优先级 | 阶段 | 价值 | 改动量 |
|--------|------|------|--------|
| P0 | Phase 1（源码断点）+ Phase 2（变量查看） | 80% 调试价值 | 小 |
| P1 | Phase 4（调用栈） | 崩溃定位关键 | 中 |
| P2 | Phase 5（异常栈） | Python 语义匹配 | 中 |
| P3 | Phase 3（单步） | 体验提升 | 中（依赖 Phase 1） |
| P4 | Phase 6（硬错误） | 兜底保护 | 中 |
| P5 | DWARF 生成 | GDB 互操作 | 大（可选） |

---

## 8. 风险与考量

### 8.1 性能开销

- **断点桩**：仅 `--debug` 构建启用，release 完全剥离
- **IR 膨胀**：每行插入断点桩约增加 10-20% IR 体积，可接受
- **运行时开销**：断点桩检查为单次数组访问 + 分支预测，开销极低
- **栈帧维护**：每函数调用增加 2 次 `push/pop_frame`，约 5-10% 调用开销

### 8.2 兼容性

- **SHA1 命名**：vdb 需理解 SHA1 前缀，通过 `ModuleSha1` 字段还原为 `模块名.函数名` 显示
- **泛型特化**：`Stack[int]` 需作为完整类型名处理，TypeMeta 中保留特化名
- **跨模块**：`.tpd` 文件按模块生成，vdb 启动时合并所有相关 `.tpd`
- **`main` 特化**：`main` 函数无 SHA1 头，vdb 需特殊处理

### 8.3 安全性

- **栈帧分配**：使用 `mb.alloc` 而非 `c.malloc`，确保与 TransPyC 内存管理一致
- **信号处理器**：异步信号中避免调用非异步信号安全函数，仅设置标志位

### 8.4 限制

- **优化构建**：高优化级别（`-O2`+）下变量可能被优化掉，`.tpd` 中的 VarBinding 可能失效
- **内联函数**：内联后栈帧信息丢失，需在 `.tpd` 中标记 `IsInline`
- **尾调用优化**：破坏调用栈链，需禁用或特殊处理

---

## 9. 文件结构规划

```
lib/vdb/                          # vdb 调试器前端
├── __init__.py
├── TpdLoader.py                  # .tpd 文件加载与合并
├── BreakpointManager.py          # 断点管理
├── DebugSession.py               # 调试会话主循环
├── CommandParser.py              # 命令解析
├── SourceView.py                 # 源码显示
├── ValueRenderer.py              # 变量值渲染
├── StackWalker.py                # 调用栈遍历
└── IpcChannel.py                 # 与被调试进程的 IPC 通信

lib/core/LLVMCG/
└── DebugInfoGen.py               # 新增：.tpd 生成器（编译器侧）

includes/
└── vdb_runtime.py                # 运行时断点/栈帧/信号处理库
```

---

## 10. 与现有系统的集成点

### 10.1 编译器集成

- **`Phase2Translator.py`**：在生成 `.ll` 后，调用 `DebugInfoGen.GenerateTpd(module, gen, output_path)`
- **`BaseGen.py`**：`_set_node_info` 扩展记录 SourceMap；函数编译结束序列化 VarScope
- **`HandlesFunctions.py`**：函数入口/出口插入 `__tpc_push_frame`/`__tpc_pop_frame`（仅 debug 模式）
- **`HandlesRaise.py`**：`_HandleRaiseLlvm` 插入 `__tpc_capture_trace`

### 10.2 project.json 配置

新增 `debug` 选项：

```json
{
  "options": {
    "debug": true,
    "debug_info": "tpd"
  }
}
```

- `debug: true` — 启用调试构建（插入断点桩、栈帧维护、信号处理）
- `debug_info: "tpd"` — 生成 .tpd 文件（默认）
- `debug_info: "dwarf"` — 生成 DWARF（未来支持）
- `debug_info: "both"` — 同时生成（未来支持）

### 10.3 命令行集成

```bash
# 编译带调试信息
transpyc build --debug

# 直接启动调试器
transpyc debug ./output/MyApp.exe
# 等价于
vdb ./output/MyApp.exe
```

---

## 11. 开放问题

1. **llvmlite DIBuilder 支持度**：需验证 llvmlite 是否暴露 DIBuilder 接口，决定 DWARF 生成可行性
2. **IPC 协议**：vdb 前端与被调试进程的通信方式（管道/Socket/共享内存）待定
3. **远程调试**：是否支持远程调试（如 ViperOS 裸机目标）需进一步设计
4. **表达式求值**：`print x.y.z` 等复杂表达式是否需要完整 Python 解释器，还是简化为路径访问
5. **多线程**：TransPyC 当前是否支持多线程，调试器如何处理线程切换

---

## 12. 参考文档

- 现状分析：基于 2026-06-26 对 `lib/core/` 和 `lib/Projectrans/` 的完整搜索
- 异常机制：`wiki/09-exceptions.md`
- 导入机制：`wiki/10-imports.md`
- 符号表：`lib/core/SymbolTable.py`
- 类型系统：`lib/core/TypeSpec.py` / `lib/core/Handles/HandlesBase.py`
- LLVM 生成：`lib/core/LLVMCG/BaseGen.py` / `FuncGen.py` / `MemoryOps.py`

---

> **本文档为设计阶段产物，暂不实现。待社区需求或开发优先级提升后，按 Phase 1 → 2 → 4 → 5 → 3 → 6 顺序推进。**
