痞子衡嵌入式:內存讀寫正確性壓力測試程序(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也是當前最流行的版本。

核心程序下載: http://pyropus.ca/software/memtester/

  核心程序包下載后,在\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>[B|K|M|G] [loops]\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[i].name) break;
            if (testmask && (!((1 << i) & testmask))) {
                continue;
            }
            printf("  %-20s: ", tests[i].name);
            // 可以看到將內存測試總空間一分為二,傳給子程序做處理的
            if (!tests[i].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個函數,涉及各種內存訪問經驗操作,具體可以看網上的一篇詳細解析文章 https://www.jianshu.com/p/ef203c360c4f。

測試函數名 測試作用
test_stuck_address 先全部把地址值交替取反放入對應存儲位置,然后再讀出比較,重復n次
test_random_value 等效test_random_comparison(bufa, bufb, count):數據敏感型測試用例
test_xor_comparison 與test_random_value比多了個異或操作
test_sub_comparison 與test_random_value比多了個減法操作
test_mul_comparisone 與test_random_value比多了個乘法操作
test_div_comparison 與test_random_value比多了個除法操作
test_or_comparison 在test_random_comparison()里面合並了
test_and_comparison 在test_random_comparison()里面合並了
test_seqinc_comparison 是 test_blockseq_comparison的一個子集;模擬客戶壓力測試場景
test_solidbits_comparison 固定全1后寫入兩個buffer,然后讀出比較,然后全0寫入讀出比較;這就是Zero-One算法
test_blockseq_comparison 一次寫一個count大小的塊,寫的值是拿byte級的數填充32bit,然后取出對比,接着重復256次;也是壓力用例,只是次數變多了;
test_checkerboard_comparison 把設定好的幾組Data BackGround,依次寫入,然后讀出比較
test_bitspread_comparison 還是在32bit里面移動,只是這次移動的不是單單的一個0或者1,而是兩個1,這兩個1之間隔着兩個空位/td>
test_bitflip_comparison 也是32bit里面的一個bit=1不斷移動生成data pattern然后,每個pattern均執行
test_walkbits1_comparison 與test_walkbits0_comparison同理
test_walkbits0_comparison 就是bit=1的位置在32bit里面移動,每移動一次就全部填滿buffer,先是從低位往高位移,再是從高位往低位移動
test_8bit_wide_random 以char指針存值,也就是每次存8bit,粒度更細;
test_16bit_wide_random 以unsigned short指針存值,也就是每次存16bit,不同粒度檢測;

2.4 結果格式

  在Unix-like系統下使用make && make install命令進行編譯可得到一個可執行的memtester,可以隨便執行memtester 10M 1,即申請10M的內存測試1次,結果如下:

[root@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)
got  10MB (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

Done.

  至此,內存讀寫正確性壓力測試程序memtester痞子衡便介紹完畢了,掌聲在哪里~~~

歡迎訂閱

文章會同時發布到我的 博客園主頁CSDN主頁知乎主頁微信公眾號 平台上。

微信搜索"痞子衡嵌入式"或者掃描下面二維碼,就可以在手機上第一時間看了哦。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM