Edit online

内存检查工具

Read time: 5 minute(s)

在用户态程序开发过程中,诸如内存泄漏、越界等问题较难定位,使用第三方工具可以提高问题定位效率,如下所示:

名称 优点 缺点
valgrind 功能强大。 需要使用源码编译;对 RISCV 支持不够好;运行时对性能影响较大。
ASAN 使用简单,只需要添加编译选项。 Luban 的工具链不支持 ASAN。
mtrace 使用简单,只需增加一个头文件和编译选项。 查看结果不方便,需要使用 mtrace 工具转换,Luban 的工具链中没有该工具。
memwatch 使用简单,编译时添加两个文件和编译选项。 -

在 Luban 系统中,推荐使用 memwatch。

Edit online

memwatch

Read time: 5 minute(s)
memwatch 是一个检查内存使用不当等问题的工具,包括检测多次释放、错误释放、内存泄漏、溢出等内存问题。memwatch 的使用说明如下:
  1. 分别下载 memwatch.cmemwatch.h 文件。
  2. 将下载的两个文件加入测试工程代码并重新编译,且在编译时需添加编译选项 -DMEMWATCH –DMW_STDIO。
  3. 将编译后的可执行文件和动态库推入板子。
  4. 按照正常流程运行程序,执行过程中会在当前目录下生成 memwatch.log

检测内存泄露

以下是检测内存泄露的相关代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "memwatch.h"

int main(int argc, char **argv)
{
    char *buf1 = (char *)malloc(2048);
    memset(buf1, 0, 2048);

    char *buf2 = (char *)malloc(2048);
    memset(buf2, 0, 2048);
    buf2[2052] = 10;

    free(buf2);
    return 0;
}

详细检测内存泄露的步骤流程如下所示:

  1. 将上述代码文件和 memwatch.cmemwatch.h 放在一个目录下,执行以下命令进行编译:
    gccmemwatch.ctest.c-DMEMWATCH-DMW_STDIO -o test
  2. 执行./test 命令。

    执行上述命令后,会在当前目录下生成 memwatch.log 文件,示例如下,其中第 8 行代码显示申请的内存没有释放,第 15 行代码检测到内存溢出:

    ============= MEMWATCH 2.71 Copyright (C) 1992-1999 Johan Lindh =============
    
    Started at Wed Jul 12 11:31:33 2023
    
    Modes: __STDC__ 64-bit mwDWORD==(unsigned int)
    mwROUNDALLOC==8 sizeof(mwData)==56 mwDataSize==56
    
    overflow: <3> test.c(15), 2048 bytes alloc'd at <2> test.c(11)
    
    Stopped at Wed Jul 12 11:31:33 2023
    
    unfreed: <1> test.c(8), 2048 bytes at 0x1d7e640         {00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................}
    
    Memory usage statistics (global):
     N)umber of allocations made: 2
     L)argest memory usage      : 4096
     T)otal of all alloc() calls: 4096
     U)nfreed bytes totals      : 2048

注意事项

内存越界检查的原理如下:
  • 申请内存时,在实际使用内存前后会多分配 8 个字节,并为其赋初值。
  • 释放内存时,通过检查多余的几个字节是否为初值来判断是否存在内存越界。

例如,以下示例不能检测到内存越界:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "memwatch.h"

int main(int argc, char **argv)
{
    char *buf1 = (char *)malloc(2048);
    memset(buf1, 0, 2048);

    buf1[3000] = 10;

    free(buf1);
    return 0;
}
Edit online

valgrind

Read time: 5 minute(s)

valgrind (官网地址:https://valgrind.org)是一个用于内存调试、内存检测以及性能分析的工具集合,包含如下工具:

  • Memcheck:最常用的工具,用于检测程序中的内存问题。

  • Cachegrind:cache 分析器,能够提供程序中 cache 丢失和命中次数以及每个函数、每个模块产生的指令数,对于性能优化有很大帮助。

  • Callgrind:Cachegrind 的扩展功能,能够收集 Cachegrind 中所有数据信息。

  • Helgrind:用于检查多线程程序中出现的竞争问题。

  • Massif:堆栈分析器,能够提供程序运行中使用的堆栈大小。

valgrind 详细使用说明如下:

  1. 从 github 下载代码,地址 (https://github.com/petrpavlv/valgrind-riscv64);
    注: 官网的代码暂不支持 RISCV,上述 github 中支持 RISCV 版本,但兼容性有待完善。
  2. 使用 Luban SDK 中的工具链编译源码:
    ./configure --host=riscv64-unknown-linux-gnu --prefix=/xxxx/valgrind-riscv64-riscv64-linux/install
    make
    make install
  3. 把安装目录下的文件拷贝到 SD Card 中。
    注: 因为文件较大,勿直接将安装目录下的文件推到板子上。
  4. 在板子上设置 valgrind 依赖库所在路径:
    export VALGRIND_LIB=/sdcard/install/libexec/valgrind
  5. 在板子上运行:
    /sdcard/install/bin/valgrind /usr/local/bin/player_demo -i /sdcard/19031715_01.mp4
注意:
由于 github 中支持的 RISCV 版本兼容性不足,部分指令不支持,运行时可能会出现如下报错:
==300== Memcheck, a memory error detector
==300== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==300== Using Valgrind-3.21.0.GIT and LibVEX; rerun with -h for copyright info
==300== Command: /usr/local/bin/pic_test -i /sdcard/test.jpg
==300==
RISCV64 front end: standard
disInstr(riscv64): unhandled instruction 0x66F5DE0B
disInstr(riscv64): 0110'0110 1111'0101 1101'1110 0000'1011
==300== valgrind: Unrecognised instruction at address 0x40016a8.
==300==    at 0x40016A8: _dl_start (in /lib/ld-2.29.so)
==300==    by 0x9: ???
==300== Your program just tried to execute an instruction that Valgrind
==300== did not recognise.  There are two possible reasons for this.
==300== 1. Your program has a bug and erroneously jumped to a non-code
==300==    location.  If you are running Memcheck and you just saw a
==300==    warning about a bad jump, it's probably your program's fault.
==300== 2. The instruction is legitimate but Valgrind doesn't handle it,
==300==    i.e. it's Valgrind's fault.  If you think this is the case or
==300==    you are not sure, please let us know and we'll try to fix it.
==300== Either way, Valgrind will now raise a SIGILL signal which will
==300== probably kill your program.