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_start、addr_end、mode。回调类型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=...
案例操作指导
第一步:编译
第二步:烧录和验证
烧录后串口输出每次对 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);
内存监控主要用于调试阶段——定位"谁修改了这个变量的值"这类疑难问题。回调在中断上下文中执行,仅适合打印信息或记录日志,不可阻塞或延时。