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

14 KiB
Raw Permalink Blame History

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 配置段

[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 编码为 u32
  • a0-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 新标签添加规则

  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 读取接口

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 文档 本文档