1 /* 2 * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * * Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * * Neither the name of Redis nor the names of its contributors may be used 14 * to endorse or promote products derived from this software without 15 * specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include <stdlib.h> 31 #include <stdio.h> 32 #include <string.h> 33 #include <assert.h> 34 #include <limits.h> 35 #include <errno.h> 36 #include <termios.h> 37 #include <sys/ioctl.h> 38 #include "config.h" 39 40 #if (ULONG_MAX == 4294967295UL) 41 #define MEMTEST_32BIT 42 #elif (ULONG_MAX == 18446744073709551615ULL) 43 #define MEMTEST_64BIT 44 #else 45 #error "ULONG_MAX value not supported." 46 #endif 47 48 #ifdef MEMTEST_32BIT 49 #define ULONG_ONEZERO 0xaaaaaaaaUL 50 #define ULONG_ZEROONE 0x55555555UL 51 #else 52 #define ULONG_ONEZERO 0xaaaaaaaaaaaaaaaaUL 53 #define ULONG_ZEROONE 0x5555555555555555UL 54 #endif 55 56 static struct winsize ws; 57 size_t progress_printed; /* Printed chars in screen-wide progress bar. */ 58 size_t progress_full; /* How many chars to write to fill the progress bar. */ 59 60 void memtest_progress_start(char *title, int pass) { 61 int j; 62 63 printf("\x1b[H\x1b[2J"); /* Cursor home, clear screen. */ 64 /* Fill with dots. */ 65 for (j = 0; j < ws.ws_col*(ws.ws_row-2); j++) printf("."); 66 printf("Please keep the test running several minutes per GB of memory.\n"); 67 printf("Also check http://www.memtest86.com/ and http://pyropus.ca/software/memtester/"); 68 printf("\x1b[H\x1b[2K"); /* Cursor home, clear current line. */ 69 printf("%s [%d]\n", title, pass); /* Print title. */ 70 progress_printed = 0; 71 progress_full = ws.ws_col*(ws.ws_row-3); 72 fflush(stdout); 73 } 74 75 void memtest_progress_end(void) { 76 printf("\x1b[H\x1b[2J"); /* Cursor home, clear screen. */ 77 } 78 79 void memtest_progress_step(size_t curr, size_t size, char c) { 80 size_t chars = ((unsigned long long)curr*progress_full)/size, j; 81 82 for (j = 0; j < chars-progress_printed; j++) printf("%c",c); 83 progress_printed = chars; 84 fflush(stdout); 85 } 86 87 /* Test that addressing is fine. Every location is populated with its own 88 * address, and finally verified. This test is very fast but may detect 89 * ASAP big issues with the memory subsystem. */ 90 void memtest_addressing(unsigned long *l, size_t bytes) { 91 unsigned long words = bytes/sizeof(unsigned long); 92 unsigned long j, *p; 93 94 /* Fill */ 95 p = l; 96 for (j = 0; j < words; j++) { 97 *p = (unsigned long)p; 98 p++; 99 if ((j & 0xffff) == 0) memtest_progress_step(j,words*2,'A'); 100 } 101 /* Test */ 102 p = l; 103 for (j = 0; j < words; j++) { 104 if (*p != (unsigned long)p) { 105 printf("\n*** MEMORY ADDRESSING ERROR: %p contains %lu\n", 106 (void*) p, *p); 107 exit(1); 108 } 109 p++; 110 if ((j & 0xffff) == 0) memtest_progress_step(j+words,words*2,'A'); 111 } 112 } 113 114 /* Fill words stepping a single page at every write, so we continue to 115 * touch all the pages in the smallest amount of time reducing the 116 * effectiveness of caches, and making it hard for the OS to transfer 117 * pages on the swap. */ 118 void memtest_fill_random(unsigned long *l, size_t bytes) { 119 unsigned long step = 4096/sizeof(unsigned long); 120 unsigned long words = bytes/sizeof(unsigned long)/2; 121 unsigned long iwords = words/step; /* words per iteration */ 122 unsigned long off, w, *l1, *l2; 123 124 assert((bytes & 4095) == 0); 125 for (off = 0; off < step; off++) { 126 l1 = l+off; 127 l2 = l1+words; 128 for (w = 0; w < iwords; w++) { 129 #ifdef MEMTEST_32BIT 130 *l1 = *l2 = ((unsigned long) (rand()&0xffff)) | 131 (((unsigned long) (rand()&0xffff)) << 16); 132 #else 133 *l1 = *l2 = ((unsigned long) (rand()&0xffff)) | 134 (((unsigned long) (rand()&0xffff)) << 16) | 135 (((unsigned long) (rand()&0xffff)) << 32) | 136 (((unsigned long) (rand()&0xffff)) << 48); 137 #endif 138 l1 += step; 139 l2 += step; 140 if ((w & 0xffff) == 0) 141 memtest_progress_step(w+iwords*off,words,'R'); 142 } 143 } 144 } 145 146 /* Like memtest_fill_random() but uses the two specified values to fill 147 * memory, in an alternated way (v1|v2|v1|v2|...) */ 148 void memtest_fill_value(unsigned long *l, size_t bytes, unsigned long v1, 149 unsigned long v2, char sym) 150 { 151 unsigned long step = 4096/sizeof(unsigned long); 152 unsigned long words = bytes/sizeof(unsigned long)/2; 153 unsigned long iwords = words/step; /* words per iteration */ 154 unsigned long off, w, *l1, *l2, v; 155 156 assert((bytes & 4095) == 0); 157 for (off = 0; off < step; off++) { 158 l1 = l+off; 159 l2 = l1+words; 160 v = (off & 1) ? v2 : v1; 161 for (w = 0; w < iwords; w++) { 162 #ifdef MEMTEST_32BIT 163 *l1 = *l2 = ((unsigned long) v) | 164 (((unsigned long) v) << 16); 165 #else 166 *l1 = *l2 = ((unsigned long) v) | 167 (((unsigned long) v) << 16) | 168 (((unsigned long) v) << 32) | 169 (((unsigned long) v) << 48); 170 #endif 171 l1 += step; 172 l2 += step; 173 if ((w & 0xffff) == 0) 174 memtest_progress_step(w+iwords*off,words,sym); 175 } 176 } 177 } 178 179 void memtest_compare(unsigned long *l, size_t bytes) { 180 unsigned long words = bytes/sizeof(unsigned long)/2; 181 unsigned long w, *l1, *l2; 182 183 assert((bytes & 4095) == 0); 184 l1 = l; 185 l2 = l1+words; 186 for (w = 0; w < words; w++) { 187 if (*l1 != *l2) { 188 printf("\n*** MEMORY ERROR DETECTED: %p != %p (%lu vs %lu)\n", 189 (void*)l1, (void*)l2, *l1, *l2); 190 exit(1); 191 } 192 l1 ++; 193 l2 ++; 194 if ((w & 0xffff) == 0) memtest_progress_step(w,words,'='); 195 } 196 } 197 198 void memtest_compare_times(unsigned long *m, size_t bytes, int pass, int times) { 199 int j; 200 201 for (j = 0; j < times; j++) { 202 memtest_progress_start("Compare",pass); 203 memtest_compare(m,bytes); 204 memtest_progress_end(); 205 } 206 } 207 208 void memtest_test(size_t megabytes, int passes) { 209 size_t bytes = megabytes*1024*1024; 210 unsigned long *m = malloc(bytes); 211 int pass = 0; 212 213 if (m == NULL) { 214 fprintf(stderr,"Unable to allocate %zu megabytes: %s", 215 megabytes, strerror(errno)); 216 exit(1); 217 } 218 while (pass != passes) { 219 pass++; 220 221 memtest_progress_start("Addressing test",pass); 222 memtest_addressing(m,bytes); 223 memtest_progress_end(); 224 225 memtest_progress_start("Random fill",pass); 226 memtest_fill_random(m,bytes); 227 memtest_progress_end(); 228 memtest_compare_times(m,bytes,pass,4); 229 230 memtest_progress_start("Solid fill",pass); 231 memtest_fill_value(m,bytes,0,(unsigned long)-1,'S'); 232 memtest_progress_end(); 233 memtest_compare_times(m,bytes,pass,4); 234 235 memtest_progress_start("Checkerboard fill",pass); 236 memtest_fill_value(m,bytes,ULONG_ONEZERO,ULONG_ZEROONE,'C'); 237 memtest_progress_end(); 238 memtest_compare_times(m,bytes,pass,4); 239 } 240 } 241 242 void memtest_non_destructive_invert(void *addr, size_t size) { 243 volatile unsigned long *p = addr; 244 size_t words = size / sizeof(unsigned long); 245 size_t j; 246 247 /* Invert */ 248 for (j = 0; j < words; j++) 249 p[j] = ~p[j]; 250 } 251 252 void memtest_non_destructive_swap(void *addr, size_t size) { 253 volatile unsigned long *p = addr; 254 size_t words = size / sizeof(unsigned long); 255 size_t j; 256 257 /* Swap */ 258 for (j = 0; j < words; j += 2) { 259 unsigned long a, b; 260 261 a = p[j]; 262 b = p[j+1]; 263 p[j] = b; 264 p[j+1] = a; 265 } 266 } 267 268 void memtest(size_t megabytes, int passes) { 269 if (ioctl(1, TIOCGWINSZ, &ws) == -1) { 270 ws.ws_col = 80; 271 ws.ws_row = 20; 272 } 273 memtest_test(megabytes,passes); 274 printf("\nYour memory passed this test.\n"); 275 printf("Please if you are still in doubt use the following two tools:\n"); 276 printf("1) memtest86: http://www.memtest86.com/\n"); 277 printf("2) memtester: http://pyropus.ca/software/memtester/\n"); 278 exit(0); 279 } 280