This commit is contained in:
xyou
2026-07-31 13:53:49 +08:00
parent 5ea84fac97
commit f52157b7af
241 changed files with 26843 additions and 8155 deletions
+412
View File
@@ -0,0 +1,412 @@
# Deshab 内核插桩系统设计文档
> 文档版本: 1.0
> 创建日期: 2026-07-29
> 路径: docs/RE/插桩系统设计.md
---
## 1. 概述
### 1.1 设计目标
为 Deshab 内核及各子系统建立统一的插桩基础设施,解决以下问题:
| 问题 | 现状 | 插桩方案 |
|------|------|----------|
| 无运行时日志过滤 | 所有 `log_info/warn/error` 无条件输出到串口 | `INSTR_LOG` 带级别+模块过滤 |
| 无结构化事件记录 | 仅线性串口输出,无法事后回溯 | `INSTR_PROBE` 环形探测缓冲 |
| 无轻量级统计 | 热路径缺乏量化数据 | `INSTR_STAT` 计数器+周期摘要 |
| 无跨模块统一接口 | UTSM/DKM/DSK 各用不同日志接口 | 统一 `INSTR_*` 宏体系 |
| 无配置控制 | 插桩代码无法运行时开关 | FUCK `[debug]` 配置段 |
### 1.2 设计原则
1. **零开销禁用**: `g_instr_enabled=0` 时所有宏展开为空操作,不引入任何分支或内存访问
2. **热路径安全**: `STAT_INC` 仅 `if(enabled) count++`,一条条件分支;`PROBE` 仅条件写入
3. **统一接口**: 所有 UTSM 模块使用 `INSTR_*` 宏;DKM 驱动使用 `DKM_*` 宏
4. **配置驱动**: 通过 FUCK `[debug]` 段控制开关与粒度,无需重编译
5. **标签编码**: 4 字符 ASCII 编码为 u32,避免字符串比较开销
---
## 2. FUCK `[debug]` 配置说明
### 2.1 配置段
```ini
[debug]
# 全局插桩开关 (0=禁用,零开销; 1=启用所有 INSTR_* 宏)
enabled=0
# 日志级别阈值
# 0=TRACE, 1=DEBUG, 2=INFO, 3=WARN, 4=ERROR, 5=SILENT
# 仅 >= log_level 的 INSTR_LOG 消息才输出
log_level=2
# 探测缓冲开关 (0=禁用; 1=启用 INSTR_PROBE 写入环形缓冲)
probe_enable=0
# 探测缓冲容量 (条目数,每条 40 字节)
# 默认 4096 条 ≈ 160KB
probe_capacity=4096
# 统计摘要间隔 (每 N 次增量输出一次摘要)
stat_summary_interval=256
# 模块级过滤: 1=启用该模块的 TRACE/DEBUG 级别日志
utsm_trace=0
vmm_trace=0
dkm_trace=0
dsk_trace=0
block_trace=0
net_trace=0
input_trace=0
```
### 2.2 启用方式
**调试启动性能**:
```ini
[debug]
enabled=1
log_level=1
# 启动时观察各阶段耗时
```
**调试 VMM VM-Exit**:
```ini
[debug]
enabled=1
log_level=1
probe_enable=1
vmm_trace=1
```
**调试 AHCI DMA**:
```ini
[debug]
enabled=1
log_level=1
dkm_trace=1
block_trace=1
```
---
## 3. 宏系统设计
### 3.1 INSTR_LOG — 带级别和模块过滤的日志
```c
INSTR_LOG(level, mod_flag, msg)
```
- `level`: `INSTR_LOG_TRACE(0)` / `DEBUG(1)` / `INFO(2)` / `WARN(3)` / `ERROR(4)` / `SILENT(5)`
- `mod_flag`: `INSTR_F_xxx` 模块标志位,或 `0` 表示不过滤
- `msg`: 常量字符串
**过滤逻辑**:
```
g_instr_enabled && level >= g_instr_log_level && (mod_flag==0 || mod_flag & g_instr_module_flags)
```
### 3.2 INSTR_STAT — 轻量级计数器
```c
INSTR_STAT_DECL(name) // 声明: static u64 name_count + name_last_ts
INSTR_STAT_INC(name) // 自增: if(enabled) name_count++
INSTR_STAT_SUMMARY(name) // 摘要: 达到间隔时输出并重置
```
**设计要点**:
- `STAT_INC` 仅 `if(enabled) count++`,一条条件分支,极轻量
- `STAT_SUMMARY` 在 `count >= stat_interval` 时输出,自动重置
- 适合热路径:VM-Exit 分发、IRQ handler、网络收发
### 3.3 INSTR_PROBE — 环形探测缓冲
```c
INSTR_PROBE(tag, a0, a1, a2, a3)
```
- `tag`: 标签常量 `INSTR_TAG_xxx`,4 字符 ASCII 编码为 u32
- `a0-a3`: 4 个 u64 参数,语义由标签定义
**探测条目结构** (40 字节/条):
```c
typedef struct probe_entry {
u32 tag; // 4字符ASCII标签
u32 tick; // 低32位TSC(~2s溢出@2GHz,足够诊断)
u64 args[4]; // 4个u64参数
} probe_entry;
```
**环形缓冲行为**:
- 容量由 `probe_capacity` 控制(默认 4096 条)
- 写入时 head 指针推进,满时覆盖最旧记录
- 通过 `probe_read(n, out)` 读取最近 N 条
- 通过 shell `$probe` 命令读取(Phase 4 实现)
### 3.4 INSTR_TS — 时间戳标注
```c
INSTR_TS_DECL(name) // 声明: static u64 name_ts
INSTR_TS_BEGIN(name) // 记录: name_ts = instr_rdtsc()
INSTR_TS_END(name, label) // 输出: "[TS] label elapsed_ms=0x..."
```
**设计要点**:
- 使用 `instr_tsc_per_ms`(独立于 serial.c 的 TSC 校准)
- 仅在 `g_instr_enabled` 时输出
- 适合启动阶段耗时测量
---
## 4. 标签编码约定
### 4.1 编码规则
4 字符 ASCII 大端序编码为 u32:
```
'B'(0x42) 'O'(0x4F) 'O'(0x4F) 'T'(0x54) → 0x424F4F54
```
### 4.2 标签注册表
| 标签常量 | 编码 | 用途 | 参数语义 |
|----------|------|------|----------|
| `INSTR_TAG_BOOT` | 0x424F4F54 | 通用启动事件 | a0=vendor[0:1] |
| `INSTR_TAG_IDT0` | 0x49445430 | IDT/中断初始化 | — |
| `INSTR_TAG_ARE0` | 0x41524530 | Arena 初始化 | — |
| `INSTR_TAG_DMA0` | 0x444D4130 | DMA 初始化 | — |
| `INSTR_TAG_NET0` | 0x4E455430 | 网络注册 | — |
| `INSTR_TAG_DRR0` | 0x44525230 | DRR 初始化 | — |
| `INSTR_TAG_UTS0` | 0x55545330 | UTSM 核心初始化 | — |
| `INSTR_TAG_DKM0` | 0x444B4D30 | DKM 加载 | — |
| `INSTR_TAG_DSM0` | 0x44534D30 | 驱动 manifest 加载 | — |
| `INSTR_TAG_DSK0` | 0x44534B30 | DSK 加载跳转 | — |
| `INSTR_TAG_SELF` | 0x53454C46 | 自检 | — |
| `INSTR_TAG_VMM0` | 0x564D4D30 | VMM 初始化 | — |
| `INSTR_TAG_LIN0` | 0x4C494E30 | Linux loader | — |
| `INSTR_TAG_VME0` | 0x564D4530 | VM-Exit 事件 | a0=reason |
| `INSTR_TAG_EPTV` | 0x45505456 | EPT violation | a0=GPA |
| `INSTR_TAG_EPTM` | 0x4550544D | EPT misconfig | a0=GPA |
| `INSTR_TAG_AHCI` | 0x41484349 | AHCI 事件 | a0=port |
| `INSTR_TAG_E1K0` | 0x45314B30 | e1000 事件 | a0=IRQ type |
| `INSTR_TAG_FAT0` | 0x46415430 | FAT32 事件 | a0=operation |
| `INSTR_TAG_NVM0` | 0x4E564D30 | NVMe 事件 | a0=BAR |
| `INSTR_TAG_IRQ0` | 0x49525130 | IRQ 注册/触发 | a0=vector |
| `INSTR_TAG_PIT0` | 0x50495430 | PIT 模拟 | a0=divisor |
| `INSTR_TAG_SRL0` | 0x53524C30 | 串口事件 | — |
| `INSTR_TAG_MMIO` | 0x4D4D494F | MMIO 访问 | a0=GPA |
| `INSTR_TAG_HCALL` | 0x4843414C | Hypercall | a0=nr, a1=arg1 |
| `INSTR_TAG_EXCP` | 0x45584350 | CPU 异常 | a0=vector, a1=err |
### 4.3 新标签添加规则
1. 选择 4 个大写 ASCII 字符,语义明确
2. 以 `INSTR_TAG_` 为前缀定义常量
3. 在上表注册,注明参数语义
4. 避免与已有标签冲突
---
## 5. 各模块插桩清单
### 5.1 UTSM 内核 (`CODE/UTSM/kernel/main.c`)
| 插桩点 | 类型 | 说明 |
|--------|------|------|
| `kernel_main()` 入口 | INSTR_TS | 启动总时间戳 |
| CPU 检测完成 | INSTR_PROBE(BOOT) | vendor/phys_bits |
| `idt_init()` | INSTR_TS | IDT+PIC 初始化耗时 |
| FUCK 配置加载 | INSTR_PROBE(BOOT) | 加载成功/失败 |
| `arena_init()` | INSTR_TS | Arena 初始化耗时 |
| `instr_init()` | — | 插桩系统初始化(含探测缓冲) |
| `dma_init()` | INSTR_TS | DMA 初始化耗时 |
| `net_init()` | INSTR_TS | 网络注册耗时 |
| `drr_init()` | INSTR_TS | DRR 初始化耗时 |
| `utsm_init()` | INSTR_TS | UTSM 核心初始化耗时 |
| `dkm_init()` + fill_platform | INSTR_TS | DKM 加载耗时 |
| `dsm_load_by_manifest_ex()` | INSTR_TS | 驱动 manifest 加载耗时 |
| `utsm_selftest_run()` | INSTR_TS | 自检耗时 |
| `vmm_init()` + self_test | INSTR_TS | VMM 初始化+自检耗时 |
| `linux_loader_init()` + launch | INSTR_TS | Linux 加载耗时 |
| `dsk_load_and_jump()` | INSTR_PROBE(DSK0) | DSK 跳转 |
### 5.2 VMM VM-Exit (`CODE/UTSM/vmm/vmexit.c`)
| 插桩点 | 类型 | 说明 |
|--------|------|------|
| `vmexit_dispatch()` 入口 | INSTR_STAT(vmexit_total) | VM-Exit 总计 |
| `EXIT_EPT_VIOLATION` | INSTR_STAT(ept_violation) | EPT violation 统计 |
| `EXIT_EPT_MISCONFIG` | INSTR_PROBE(EPTM) | EPT 配置错误记录 |
| `EXIT_EXCEPTION_NMI` | INSTR_PROBE(EXCP) | CPU 异常记录 |
| `EXIT_TRIPLE_FAULT` | INSTR_PROBE(EXCP) | 三重故障记录 |
| `EXIT_EXTERNAL_INTERRUPT` | INSTR_STAT(ext_irq) | 外部中断统计 |
| `EXIT_CPUID` | INSTR_STAT(cpuid_exit) | CPUID 统计 |
| `EXIT_IO_INSTRUCTION` | INSTR_STAT(io_exit) | I/O 端口统计 |
| `EXIT_INTERRUPT_WINDOW` | INSTR_STAT(intr_window) | 中断窗口统计 |
| `EXIT_VMX_PREEMPTION_TIMER` | INSTR_STAT(preempt_timer) | 抢占定时器统计 |
| `EXIT_RDMSR/WRMSR` | INSTR_STAT(msr_exit) | MSR 访问统计 |
| `EXIT_VMCALL` | INSTR_PROBE(HCALL) | Hypercall 记录 |
| `vmexit_before_resume()` | INSTR_STAT(pre_resume) | resume 前处理统计 |
### 5.3 IDT (`CODE/UTSM/arch/x86_64/idt.c`)
| 插桩点 | 类型 | 说明 |
|--------|------|------|
| CPU 异常 (vector<32) | INSTR_PROBE(EXCP) | vector, err_code, rip |
| PIC IRQ (32-47) | INSTR_STAT(pic_irq_total) | PIC IRQ 总计 |
| APIC IRQ (48-255) | INSTR_STAT(apic_irq_total) | APIC/IOAPIC/MSI IRQ |
| `irq_register()` | INSTR_LOG(DEBUG) | IRQ handler 注册 |
### 5.4 DKM 驱动
每个驱动在 `driver_init` 开头调用 `dkm_instr_init(api)` 初始化开关。
| 驱动 | 插桩 | 说明 |
|------|------|------|
| **AHCI** | DKM_STAT/DKM_TS | PCI 发现、ABAR 映射、DMA 操作 |
| **e1000** | DKM_STAT | IRQ、RX/TX 统计、链路变化 |
| **FAT32** | DKM_STAT | BPB 解析、FAT 遍历、文件读写 |
| **NVMe** | DKM_STAT/DKM_TS | PCI 发现、BAR 解析 |
| **ps2kbd** | DKM_STAT | IRQ1 统计、扫描码计数 |
### 5.5 DSK / FirstInit / mouseInit
DSK 系列模块是独立 ELF,使用自有最小串口/TSC 接口:
| 模块 | 插桩 | 说明 |
|------|------|------|
| **DSK** | DSK_TS/DSK_STAT | FAT32 读取耗时、ELF 加载、模块调度 |
| **FirstInit** | logl 增强 | 按键字段切换日志 |
| **mouseInit** | 计数器 | IRQ12 包统计(已有日志) |
---
## 6. 探测缓冲系统设计
### 6.1 数据结构
```
probe_entry (40 bytes):
┌──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┐
│ tag │ tick │ args[0] │ args[1] │ args[2] │ args[3] │
│ u32 │ u32 │ u64 │ u64 │ u64 │ u64 │
└──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┘
```
### 6.2 环形缓冲管理
```
g_probe_buf[0..cap-1] ← arena 分配的连续内存
g_probe_head ← 下一条写入位置(单调递增)
g_probe_count ← 已写入总条目数
写入: idx = head % cap; buf[idx] = entry; head++; count++;
读取: 从 (head - n) 到 head,按时间顺序
```
### 6.3 读取接口
```c
int probe_read(u32 n, probe_entry *out); // 读取最近 N 条
u32 probe_count(void); // 已写入总条目数
u32 probe_capacity(void); // 缓冲容量
```
### 6.4 Shell 命令
```
probe — 显示最近 16 条探测记录
probe 64 — 显示最近 64 条探测记录
probe stat — 显示探测缓冲统计信息
probe tags — 显示所有已知标签
```
**实现方式**: UTSM 在 `dsk_fill_boot_context()` 中通过 `reserved[6]` 传递 `probe_info` 结构体指针,
shell 从中读取探测缓冲地址并直接访问共享内存。`probe_info` 结构定义在 `dsk.h` 中:
```c
typedef struct probe_info {
u32 magic; /* PROBE_INFO_MAGIC = 0x50524F42 ("PRFB") */
u32 version; /* ABI 版本,当前 1 */
u64 probe_buf_addr; /* probe_entry 缓冲基地址 */
u32 probe_cap; /* 缓冲容量(条目数) */
u32 probe_head; /* 当前写入位置 */
u32 probe_count; /* 已写入总条目数 */
u32 probe_dropped; /* 覆盖条目数 */
u32 instr_enabled; /* 插桩开关快照 */
u32 instr_probe_enable; /* 探测开关快照 */
u64 reserved[4];
} probe_info;
```
---
## 7. 零开销设计说明
### 7.1 禁用时展开
当 `g_instr_enabled=0`(默认配置)时:
```c
INSTR_LOG(level, mod_flag, msg)
→ if (0 && ...) { ... } // 编译器优化为空操作
INSTR_STAT_INC(name)
→ if (0) { name_count++; } // 编译器优化为空操作
INSTR_PROBE(tag, a0, a1, a2, a3)
→ if (0 && 0) probe_write(...) // 编译器优化为空操作
INSTR_TS_BEGIN(name) / INSTR_TS_END(name, label)
→ name_ts = rdtsc(); // 写入 static 变量(可优化掉)
→ if (0) { ... } // 编译器优化为空操作
```
### 7.2 热路径分析
**vmexit_dispatch** (每秒可能数千次):
- `INSTR_STAT_INC(vmexit_total)`: `if(enabled) count++` → 一条条件自增
- 所有 STAT_INC 合计: 1 条条件分支 + 1 条自增
- 无 PROBE 写入(仅在 `probe_enable=1` 时触发)
- 无 LOG 输出(仅在 `log_level <= DEBUG` 时触发)
**idt_handler** (每次中断/异常):
- `INSTR_STAT_INC(pic_irq_total)`: 一条条件自增
- `INSTR_PROBE(EXCP)`: 仅 CPU 异常时触发(极低频)
---
## 8. 实施路线图
| Phase | 内容 | 文件 | 状态 |
|-------|------|------|------|
| 1 | 基础设施 | instr.h, instr.c, probe.c, dkm_instr.h, FUCK | ✅ 完成 |
| 2 | UTSM 核心 | main.c, vmexit.c, idt.c | ✅ 完成 |
| 3 | DKM 驱动 | ahci/e1000/fat32/nvme/ps2kbd | ✅ 完成 |
| 4 | DSK+Shell | dsk/main.c, shell/main.c, $probe | ✅ 完成 |
| 5 | 文档 | docs/RE/插桩系统设计.md | ✅ 本文档 |
---
## 9. 文件清单
| 文件 | 类型 | 说明 |
|------|------|------|
| `CODE/UTSM/include/utsm/instr.h` | 头文件 | 统一插桩宏定义、标签常量、probe_entry |
| `CODE/UTSM/include/utsm/dsk.h` | 头文件 | probe_info 结构定义(reserved[6] 传递) |
| `CODE/UTSM/kernel/instr.c` | 源文件 | 插桩系统初始化、全局配置、TSC 校准 |
| `CODE/UTSM/kernel/probe.c` | 源文件 | 环形探测缓冲管理 + 状态访问函数 |
| `CODE/UTSM/kernel/dsk_loader.c` | 源文件 | dsk_fill_boot_context 填充 probe_info |
| `CODE/DKM/dkm_instr.h` | 头文件 | DKM 驱动专用插桩宏(无 UTSM 依赖) |
| `SYSTEM/system/deshab64/FUCK` | 配置 | 新增 `[debug]` 配置段 |
| `CODE/UTSM/kernel/main.c` | 源文件 | 启动流程 TS+PROBE 插桩 |
| `CODE/UTSM/vmm/vmexit.c` | 源文件 | VM-Exit STAT+PROBE 插桩 |
| `CODE/UTSM/arch/x86_64/idt.c` | 源文件 | IDT STAT+PROBE 插桩 |
| `CODE/shell/main.c` | 源文件 | `probe` 命令实现(probe stat/tags/N) |
| `docs/RE/插桩系统设计.md` | 文档 | 本文档 |