You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何追踪指定指针指向的堆内存块的所有访问操作?

堆内存块访问追踪实现方案

一、需求说明

需要精确记录堆中指定指针指向的内存块的所有访问操作,输出格式要求为:

w 4b 0x12345
r 4b 0x12346
r 8b 0x12348
...

其中每一行依次表示访问类型(读/写)、访问字节数、访问地址。性能损耗可接受,核心要求是获取完整的访问地址序列。

示例代码(修正原代码错误后):

#define BLK 1024
#include <stdlib.h>
#include <stdio.h>

int monfun(int *p){
    int t;
    // 修正循环边界:BLK是字节数,需按int字节数计算循环次数
    for(int i=0; i<BLK/sizeof(int); i++){
        t=p[i];
    }
    return t;
}

int main(void){
    int *p=(int*)malloc(BLK);
    // 打印目标内存块的地址范围,用于后续日志过滤
    printf("Target block: %p to %p\n", p, (char*)p + BLK);
    monfun(p);
    free(p);
    return 0;
}

二、已尝试方案局限

使用Valgrind的massif工具仅能获取内存访问的计数器统计结果,无法提取具体的访问地址序列,无法满足需求。

三、推荐实现方案

方案1:Valgrind Memcheck(快速实现,无需额外开发)

Valgrind的Memcheck工具可追踪所有内存访问,通过日志过滤即可提取目标块的访问记录:

  1. 编译程序:保留调试信息(可选,便于定位访问来源)
    gcc -g -o mem_monitor mem_monitor.c
    
  2. 生成内存访问日志
    valgrind --tool=memcheck --log-file=memtrace.log --trace-memory=full ./mem_monitor
    
    执行后终端会输出目标块的地址范围(如Target block: 0x5204040 to 0x5204440),同时生成memtrace.log文件,包含所有内存访问的详细记录。
  3. 过滤并格式化日志
    日志中每条访问记录格式类似:
    ==12345== Memcheck: read of size 4 at 0x5204040 by thread 1
    ==12345==    at 0x109146: monfun (mem_monitor.c:8)
    ==12345==    by 0x10917A: main (mem_monitor.c:16)
    
    可通过Python、awk等脚本过滤出地址在目标范围内的记录,转换为需求格式:
    • 将read转为r、write转为w
    • 提取访问大小并添加b后缀
    • 提取访问地址并格式化

方案2:Intel Pin(精确可控,自定义输出)

Intel Pin是动态插桩工具,可精确拦截每一条内存访问指令,通过自定义回调直接生成符合要求的输出:

  1. 编写Pin工具核心代码
    #include "pin.H"
    #include <fstream>
    #include <iostream>
    
    std::ofstream traceFile;
    ADDRINT targetStart, targetEnd;
    
    // 内存访问记录回调
    VOID RecordMemAccess(VOID *addr, UINT32 size, BOOL isWrite) {
        ADDRINT addrInt = reinterpret_cast<ADDRINT>(addr);
        // 仅记录目标块内的访问
        if (addrInt >= targetStart && addrInt + size <= targetEnd) {
            traceFile << (isWrite ? "w" : "r") << " " << size << "b 0x" << std::hex << addrInt << std::dec << std::endl;
        }
    }
    
    // 插桩内存读指令
    VOID InstrumentMemRead(INS ins, VOID *v) {
        INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)RecordMemAccess,
                       IARG_MEMORYREAD_EA, IARG_MEMORYREAD_SIZE, IARG_BOOL, FALSE, IARG_END);
    }
    
    // 插桩内存写指令
    VOID InstrumentMemWrite(INS ins, VOID *v) {
        INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)RecordMemAccess,
                       IARG_MEMORYWRITE_EA, IARG_MEMORYWRITE_SIZE, IARG_BOOL, TRUE, IARG_END);
    }
    
    // 参数说明
    INT32 Usage() {
        std::cerr << "Usage: pin -t <tool.so> -- ./program <target_start> <target_end>" << std::endl;
        return -1;
    }
    
    int main(int argc, char *argv[]) {
        if (PIN_Init(argc, argv)) return Usage();
    
        // 从参数获取目标块地址范围
        targetStart = strtoul(argv[argc-2], NULL, 16);
        targetEnd = strtoul(argv[argc-1], NULL, 16);
    
        traceFile.open("mem_access_trace.log");
        if (!traceFile.is_open()) {
            std::cerr << "Failed to open trace file" << std::endl;
            return -1;
        }
    
        // 注册插桩回调
        INS_AddInstrumentFunction(InstrumentMemRead, NULL);
        INS_AddInstrumentFunction(InstrumentMemWrite, NULL);
    
        PIN_StartProgram();
        return 0;
    }
    
  2. 编译并运行
    参考Intel Pin官方文档完成工具编译,之后执行:
    pin -t ./memtrace.so -- ./mem_monitor 0x5204040 0x5204440
    
    执行后生成的mem_access_trace.log直接符合需求格式。

其他工具对比

  • radare2:适合逆向分析场景,需手动设置断点和过滤,操作繁琐。
  • qemu-mtrace:基于QEMU的内存追踪工具,记录所有访问后需额外脚本过滤目标块,灵活性不如Pin。
  • Valgrind Tracegrind:旧版本工具,新版本Valgrind已停止维护,不推荐使用。

总结

  • 追求快速实现选Valgrind Memcheck,通过日志过滤即可完成需求;
  • 需要精确自定义输出选Intel Pin,可直接生成符合格式的访问记录。

内容的提问来源于stack exchange,提问作者onlycparra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 13:55:57