跳转至

Mem Monitor

Mem Monitor 驱动 | 无 sample

学习目标

  • 理解内存监控的硬件机制——配置地址范围,CPU 访问时触发中断回调
  • 掌握 uapi_mem_monitor_init / set_section / enable 的标准配置流程
  • 理解监控模式——读监控和写监控的使用场景
  • 能够在调试阶段定位内存越界、野指针、栈溢出问题

基本概念

Mem Monitor 做什么

Memory Monitor 是硬件级的地址监控器。配置一个地址范围(起始地址 + 结束地址)和监控模式(读/写),当 CPU 对该范围内的内存进行读或写访问时,硬件自动触发中断并回调用户函数。核心用途:

  • 定位野指针:监控某块内存,谁写谁触发——回调打印 PC 和地址
  • 检测栈溢出:监控栈顶区域,写操作表示栈溢出
  • 验证数组越界:监控数组边界外,访问即触发
flowchart LR
    C[CPU] -->|读/写访问| M[地址比较器]
    M -->|地址在监控范围| I[中断触发]
    M -->|地址不在范围| N[正常访问]
    I -->|addr + mode| CB[用户回调]
    CB -->|打印地址和模式| D[调试输出]

监控模式

模式 枚举值 触发条件
读监控 MEM_MONITOR_MODE_READ CPU 读取被监控地址
写监控 MEM_MONITOR_MODE_WRITE CPU 写入被监控地址

每个 Monitor 实例只能监控一种模式。如需同时监控读和写,使用两个不同的 Monitor 实例配置同一地址范围。

与 PMP 的区别

对比项 Mem Monitor PMP
目的 调试诊断 安全保护
触发后行为 中断回调 + 继续执行 硬件异常
阶段 开发调试 生产固件
回调 有(打印信息) 无(直接异常)

涉及 API

API 用途 头文件
uapi_mem_monitor_init(void) 初始化内存监控模块 mem_monitor.h
uapi_mem_monitor_deinit(void) 去初始化 mem_monitor.h
uapi_mem_monitor_set_section(mem_monitor_t monitor, mem_monitor_config_t *config) 配置监控地址范围和模式 mem_monitor.h
uapi_mem_monitor_enable(mem_monitor_t monitor, uapi_mem_monitor_callback_t callback) 使能监控并注册回调 mem_monitor.h
uapi_mem_monitor_disable(mem_monitor_t monitor) 禁用监控 mem_monitor.h

配置结构体 mem_monitor_config_t 包含 section(段编号)、addr_startaddr_endmode。回调类型 uapi_mem_monitor_callback_t 签名为 void (*)(uint32_t addr, mem_monitor_mode_t mode)

案例说明

做什么

配置 Monitor 监控一个 64 字节全局缓冲区的写访问。使能监控后,代码中写入该缓冲区时触发回调,回调打印被访问的地址和访问模式。

规格与功能

规格项 说明
监控对象 64 字节全局数组 g_watch_buf[64]
监控模式 MEM_MONITOR_MODE_WRITE(写监控)
Monitor 实例 MEM_MONITOR_0
回调内容 打印被访问地址 + 模式

程序运行流程:init → set_section(地址范围 = buf 起止,模式 = WRITE)→ enable → 写入 buf[0] = 0x55 → 触发回调 → 回调打印地址和模式。

案例流程

sequenceDiagram
    participant U as 应用代码
    participant H as Mem Monitor硬件
    participant I as 中断
    participant C as 回调

    U->>H: uapi_mem_monitor_init
    U->>H: set_section buf[0..63] mode=WRITE
    U->>H: enable callback
    U->>U: buf[0] = 0x55
    Note over H: 检测到写操作
    H->>I: 触发中断
    I->>C: callback addr=buf mode=WRITE
    C->>C: print mem access addr=... mode=WRITE
    U->>U: buf[10] = 0xAA
    H->>I: 再次触发
    I->>C: callback addr=buf+10
    C->>C: print mem access addr=...

案例操作指导

第一步:编译

fbb build ws63-liteos-app

第二步:烧录和验证

烧录后串口输出每次对 g_watch_buf 写操作的信息——包括被写入的具体地址和模式。

第三步:预期输出

MemMonitor: init OK
MemMonitor: section set: 0x2000_1000 ~ 0x2000_1040, mode=WRITE
MemMonitor: enabled
mem access: addr=0x20001000 mode=WRITE
mem access: addr=0x2000100A mode=WRITE
mem access: addr=0x20001000 mode=WRITE

关键配置

参数 说明
地址范围 起始 < 结束 必须字节级对齐
监控模式 READ / WRITE 每次只能监控一种模式
Monitor 实例数 硬件限制 超出则 set_section 返回失败
回调耗时 < 50μs 在中断上下文执行
适用阶段 调试 生产代码通常不启用——影响性能

监控模式选择

调试目的 推荐模式 原因
谁修改了我的变量 WRITE 只关心写入操作
谁读取了敏感数据 READ 审计读访问
两者都关心 两个实例同地址 分别设置 READ 和 WRITE

代码详解

概念性代码,基于 SDK 头文件 mem_monitor.h 中的 API 签名。

#include "mem_monitor.h"
#include "errcode.h"
#include "osal_printk.h"
#include "app_init.h"

/* 被监控的缓冲区 */
static uint8_t g_watch_buf[64];

/* 内存访问回调——在中断上下文中执行 */
static void mem_access_callback(uint32_t addr, mem_monitor_mode_t mode)
{
    const char *mode_str = (mode == MEM_MONITOR_MODE_READ) ? "READ" : "WRITE";
    osal_printk("mem access: addr=0x%08X mode=%s\n", addr, mode_str);
    /* 实际调试中可在此处打印调用栈或触发断点 */
}

static void mem_monitor_entry(void)
{
    errcode_t ret;

    /* 初始化 Mem Monitor 模块 */
    ret = uapi_mem_monitor_init();
    if (ret != ERRCODE_SUCC) {
        osal_printk("MemMonitor init failed: %d\n", ret);
        return;
    }
    osal_printk("MemMonitor: init OK\n");

    /* 配置监控段:g_watch_buf 的写访问 */
    mem_monitor_config_t cfg = {
        .section    = MEM_MONITOR_SECTION_0,
        .addr_start = (uint32_t)g_watch_buf,
        .addr_end   = (uint32_t)g_watch_buf + sizeof(g_watch_buf),
        .mode       = MEM_MONITOR_MODE_WRITE
    };

    ret = uapi_mem_monitor_set_section(MEM_MONITOR_0, &cfg);
    if (ret != ERRCODE_SUCC) {
        osal_printk("MemMonitor set_section failed: %d\n", ret);
        return;
    }
    osal_printk("MemMonitor: section set: 0x%08X ~ 0x%08X, mode=WRITE\n",
                cfg.addr_start, cfg.addr_end);

    /* 使能监控并注册回调 */
    uapi_mem_monitor_enable(MEM_MONITOR_0, mem_access_callback);
    osal_printk("MemMonitor: enabled\n");

    /* ---- 以下操作将触发监控回调 ---- */

    /* 写入缓冲区——触发回调 */
    g_watch_buf[0] = 0x55;

    /* 再次写入——触发回调 */
    g_watch_buf[10] = 0xAA;

    /* 读取不会触发(当前配置为 WRITE 模式) */
    uint8_t val = g_watch_buf[0];
    (void)val;

    /* 调试完成后可禁用 */
    uapi_mem_monitor_disable(MEM_MONITOR_0);
    osal_printk("MemMonitor: disabled\n");
}

app_run(mem_monitor_entry);

内存监控主要用于调试阶段——定位"谁修改了这个变量的值"这类疑难问题。回调在中断上下文中执行,仅适合打印信息或记录日志,不可阻塞或延时。