Files
Deshab-OS/docs/RE/插桩系统设计.md
T
2026-07-31 13:53:49 +08:00

413 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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` | 文档 | 本文档 |