lsekfe 发表于 2021-8-23 11:00:36

内存读写正确性压力测试程序(memtester)

大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是内存读写正确性压力测试程序memtester。

  在嵌入式系统中,内存(RAM)的重要性不言而喻,系统性能及稳定性都与内存息息相关。关于内存性能有很多个不同指标,其中最基础的指标便是访问可靠性(即读写的正确性),只有稳定可靠的内存访问才能确保系统正常运行。很多时候简单地内存读写测试并不能发现隐藏的问题,因此我们需要一个完备的内存访问压力测试程序,今天痞子衡就和大家详细聊一聊memtester。

  一、内存性能测试程序集

  在讲memtester之前,痞子衡先给大家科普一下Linux系统下常用的内存性能测试工具,它们分别是mbw、memtester、lmbench、sysbench。这几个测试工具(程序)各有侧重点:
内存带宽测试工具            --mbw;

  内存压力测试工具            --memtester;

  内存综合性能测试工具      --lmbench;

  内存申请与读写速度测试工具   --sysbench; 二、memtester程序

  memtester是Simon Kirby在1999年编写的测试程序(v1版),后来由Charles Cazabon一直维护更新(v2及之后版本),主要面向Unix-like系统,官方主页上介绍的是“A userspace utility for testing the memory subsystem for faults.”,其实就是为了测试内存(主要DDR)的读写访问可靠性(仅正确性,与速度性能无关),这是验证板级硬件设备必不可少的一项测试。

  整个memtester测试的视角就是从用户的角度来看的,从用户角度设立不同的测试场景即测试用例,然后针对性地进行功能测试,注意是从系统级来测试,也就是说关注的不单单是内存颗粒了,还有系统板级的连线、IO性能、PCB等等相关的因素,在这些因素的影响下,内存是否还能正常工作。

  2.1 获取程序

  memtester程序的最新版本是4.5.0,早期的v1/v2/v3版本目前下载不到了,2012年Charles Cazabon重写了程序并发布了全新v4.0.0,此后一直不定期更新,v4.x也是当前最流行的版本。

  核心程序下载: pyropus.ca/software/me…

  核心程序包下载后,在\memtester-4.5.0\下可找到源代码。详细源文件目录如下:
\memtester-4.5.0

                  \memtester.h

                  \memtester.c      --主程序入口

                  \sizes.h            --关于系统位数(32/64bit)的一些定义

                  \types.h            --所用数据类型的定义

                  \tests.h

                  \tests.c            --测试算法子程序如果是移植到ARM Cortex-M平台下裸系统运行,一般只需要简单修改memtester.c文件即可,其他源文件就是一些头文件包含方面的改动,memtester本身并没有太多移植工作,其源码本是用作在Unix-like系统上运行的,而在嵌入式系统里运行仅需要把一些跟系统平台相关的代码删除即可,此外就是打印函数的实现。
  2.2 配置参数
  memtester源码里的配置选项主要是如下五个宏:
 /* 如下需用户自定义 */

  ULONG_MAX             -- 确定系统是32bit还是64bit

  TEST_NARROW_WRITES    -- 确定是否要包含8/16 bit写测试

  /* 如下借助linux头文件 */

  _SC_VERSION         -- posix system版本检查

  _SC_PAGE_SIZE         -- 内存page大小获取

  MAP_LOCKED            -- Linux里mmap里的swap特性 2.3 程序解析
  让我们尝试分析memtester主函数入口main,main()函数最开始都是一些输入参数解析,其实主要就是为了获取三个重要变量:内存测试起始地址、内存测试总长度、压力测试循环次数,有了这三个变量值之后便开始逐一跑tests.c文件里各项测试算法小函数:

struct test {

      char *name;

      int (*fp)();

  };

  struct test tests[] = {

      { "Random Value", test_random_value },

      { "Compare XOR", test_xor_comparison },

      { "Compare SUB", test_sub_comparison },

      { "Compare MUL", test_mul_comparison },

      { "Compare DIV",test_div_comparison },

      { "Compare OR", test_or_comparison },

      { "Compare AND", test_and_comparison },

      { "Sequential Increment", test_seqinc_comparison },

      { "Solid Bits", test_solidbits_comparison },

      { "Block Sequential", test_blockseq_comparison },

      { "Checkerboard", test_checkerboard_comparison },

      { "Bit Spread", test_bitspread_comparison },

      { "Bit Flip", test_bitflip_comparison },

      { "Walking Ones", test_walkbits1_comparison },

      { "Walking Zeroes", test_walkbits0_comparison },

  #ifdef TEST_NARROW_WRITES   

      { "8-bit Writes", test_8bit_wide_random },

      { "16-bit Writes", test_16bit_wide_random },

  #endif

      { NULL, NULL }

  };

  /* Function definitions */

  void usage(char *me) {

      fprintf(stderr, "\n"

              "Usage: %s [-p physaddrbase [-d device]] <mem> \n",

              me);

      exit(EXIT_FAIL_NONSTARTER);

  }

  int main(int argc, char **argv) {

      ul loops, loop, i;

      size_t bufsize, halflen, count;

      void volatile *buf, *aligned;

      ulv *bufa, *bufb;

      ul testmask = 0;

      // 省略若干变量定义代码

      printf("memtester version " __version__ " (%d-bit)\n", UL_LEN);

      printf("Copyright (C) 2001-2020 Charles Cazabon.\n");

      printf("Licensed under the GNU General Public License version 2 (only).\n");

      printf("\n");

      // 省略若干初始检查代码

      // 从输入参数里获取physaddrbase计算出内存测试起始地址aligned

      // 从输入参数里获取mem及B|K|M|G计算出内存测试总长度bufsize

      halflen = bufsize / 2;

      count = halflen / sizeof(ul);

      bufa = (ulv *) aligned;

      bufb = (ulv *) ((size_t) aligned + halflen);

      // 压力测试的重要变量, loops即重复次数

      for(loop=1; ((!loops) || loop <= loops); loop++) {

        printf("Loop %lu", loop);

        if (loops) {

              printf("/%lu", loops);

        }

        printf(":\n");

        printf("%-20s: ", "Stuck Address");

        fflush(stdout);

        // 第一个测试 stuck_address

        if (!test_stuck_address(aligned, bufsize / sizeof(ul))) {

               printf("ok\n");

        } else {

              exit_code |= EXIT_FAIL_ADDRESSLINES;

        }

        // 遍历tests.c里的所有测试子程序

        for (i=0;;i++) {

              if (!tests.name) break;

              if (testmask && (!((1 << i) & testmask))) {

                  continue;

              }

              printf("%-20s: ", tests.name);

              // 可以看到将内存测试总空间一分为二,传给子程序做处理的

              if (!tests.fp(bufa, bufb, count)) {

                  printf("ok\n");

              } else {

                  exit_code |= EXIT_FAIL_OTHERTEST;

              }

              fflush(stdout);

              /* clear buffer */

              memset((void *) buf, 255, wantbytes);

        }

        printf("\n");

        fflush(stdout);

      }

  } tests.c文件里才是最核心的压力测试算法子程序,一共17个函数,涉及各种内存访问经验操作。

2.4 结果格式
  在Unix-like系统下使用make && make install命令进行编译可得到一个可执行的memtester,可以随便执行memtester 10M 1,即申请10M的内存测试1次,结果如下:

@as150 ~] memtester 10M 1

  memtester version 4.5.0 (64-bit)

  Copyright (C) 2001-2020 Charles Cazabon.

  Licensed under the GNU General Public License version 2 (only).

  pagesize is 4096

  pagesizemask is 0xfffffffffffff000

  want 10MB (10485760 bytes)

  got10MB (10485760 bytes), trying mlock ...locked.

  Loop 1/1:

  Stuck Address: ok

  Random Value: ok

  Compare XOR: ok

  Compare SUB: ok

  Compare MUL: ok

  Compare DIV: ok

  Compare OR: ok

  Compare AND: ok

  Sequential Increment: ok

  Solid Bits: ok

  Block Sequential: ok

  Checkerboard: ok

  Bit Spread: ok

  Bit Flip: ok

  Walking Ones: ok

  Walking Zeroes: ok

  8-bit Writes: ok

  16-bit Writes: ok


页: [1]
查看完整版本: 内存读写正确性压力测试程序(memtester)