14 KiB
14 KiB
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 设计原则
- 零开销禁用:
g_instr_enabled=0时所有宏展开为空操作,不引入任何分支或内存访问 - 热路径安全:
STAT_INC仅if(enabled) count++,一条条件分支;PROBE仅条件写入 - 统一接口: 所有 UTSM 模块使用
INSTR_*宏;DKM 驱动使用DKM_*宏 - 配置驱动: 通过 FUCK
[debug]段控制开关与粒度,无需重编译 - 标签编码: 4 字符 ASCII 编码为 u32,避免字符串比较开销
2. FUCK [debug] 配置说明
2.1 配置段
[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 启用方式
调试启动性能:
[debug]
enabled=1
log_level=1
# 启动时观察各阶段耗时
调试 VMM VM-Exit:
[debug]
enabled=1
log_level=1
probe_enable=1
vmm_trace=1
调试 AHCI DMA:
[debug]
enabled=1
log_level=1
dkm_trace=1
block_trace=1
3. 宏系统设计
3.1 INSTR_LOG — 带级别和模块过滤的日志
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 — 轻量级计数器
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 — 环形探测缓冲
INSTR_PROBE(tag, a0, a1, a2, a3)
tag: 标签常量INSTR_TAG_xxx,4 字符 ASCII 编码为 u32a0-a3: 4 个 u64 参数,语义由标签定义
探测条目结构 (40 字节/条):
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 — 时间戳标注
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 新标签添加规则
- 选择 4 个大写 ASCII 字符,语义明确
- 以
INSTR_TAG_为前缀定义常量 - 在上表注册,注明参数语义
- 避免与已有标签冲突
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 读取接口
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 中:
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(默认配置)时:
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 |
文档 | 本文档 |