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 #if defined(__sun) 39 #include <stropts.h> 40 #endif 41 #include "config.h" 42 43 #if (ULONG_MAX == 4294967295UL) 44 #define MEMTEST_32BIT 45 #elif (ULONG_MAX == 18446744073709551615ULL) 46 #define MEMTEST_64BIT 47 #else 48 #error "ULONG_MAX value not supported." 49 #endif 50 51 #ifdef MEMTEST_32BIT 52 #define ULONG_ONEZERO 0xaaaaaaaaUL 53 #define ULONG_ZEROONE 0x55555555UL 54 #else 55 #define ULONG_ONEZERO 0xaaaaaaaaaaaaaaaaUL 56 #define ULONG_ZEROONE 0x5555555555555555UL 57 #endif 58 59 static struct winsize ws; 60 size_t progress_printed; /* Printed chars in screen-wide progress bar. */ 61 size_t progress_full; /* How many chars to write to fill the progress bar. */ 62 63 void memtest_progress_start(char *title, int pass) { 64 int j; 65 66 printf("\x1b[H\x1b[2J"); /* Cursor home, clear screen. */ 67 /* Fill with dots. */ 68 for (j = 0; j < ws.ws_col*(ws.ws_row-2); j++) printf("."); 69 printf("Please keep the test running several minutes per GB of memory.\n"); 70 printf("Also check http://www.memtest86.com/ and http://pyropus.ca/software/memtester/"); 71 printf("\x1b[H\x1b[2K"); /* Cursor home, clear current line. */ 72 printf("%s [%d]\n", title, pass); /* Print title. */ 73 progress_printed = 0; 74 progress_full = ws.ws_col*(ws.ws_row-3); 75 fflush(stdout); 76 } 77 78 void memtest_progress_end(void) { 79 printf("\x1b[H\x1b[2J"); /* Cursor home, clear screen. */ 80 } 81 82 void memtest_progress_step(size_t curr, size_t size, char c) { 83 size_t chars = ((unsigned long long)curr*progress_full)/size, j; 84 85 for (j = 0; j < chars-progress_printed; j++) printf("%c",c); 86 progress_printed = chars; 87 fflush(stdout); 88 } 89 90 /* Test that addressing is fine. Every location is populated with its own 91 * address, and finally verified. This test is very fast but may detect 92 * ASAP big issues with the memory subsystem. */ 93 void memtest_addressing(unsigned long *l, size_t bytes) { 94 unsigned long words = bytes/sizeof(unsigned long); 95 unsigned long j, *p; 96 97 /* Fill */ 98 p = l; 99 for (j = 0; j < words; j++) { 100 *p = (unsigned long)p; 101 p++; 102 if ((j & 0xffff) == 0) memtest_progress_step(j,words*2,'A'); 103 } 104 /* Test */ 105 p = l; 106 for (j = 0; j < words; j++) { 107 if (*p != (unsigned long)p) { 108 printf("\n*** MEMORY ADDRESSING ERROR: %p contains %lu\n", 109 (void*) p, *p); 110 exit(1); 111 } 112 p++; 113 if ((j & 0xffff) == 0) memtest_progress_step(j+words,words*2,'A'); 114 } 115 } 116 117 /* Fill words stepping a single page at every write, so we continue to 118 * touch all the pages in the smallest amount of time reducing the 119 * effectiveness of caches, and making it hard for the OS to transfer 120 * pages on the swap. */ 121 void memtest_fill_random(unsigned long *l, size_t bytes) { 122 unsigned long step = 4096/sizeof(unsigned long); 123 unsigned long words = bytes/sizeof(unsigned long)/2; 124 unsigned long iwords = words/step; /* words per iteration */ 125 unsigned long off, w, *l1, *l2; 126 127 assert((bytes & 4095) == 0); 128 for (off = 0; off < step; off++) { 129 l1 = l+off; 130 l2 = l1+words; 131 for (w = 0; w < iwords; w++) { 132 #ifdef MEMTEST_32BIT 133 *l1 = *l2 = ((unsigned long) (rand()&0xffff)) | 134 (((unsigned long) (rand()&0xffff)) << 16); 135 #else 136 *l1 = *l2 = ((unsigned long) (rand()&0xffff)) | 137 (((unsigned long) (rand()&0xffff)) << 16) | 138 (((unsigned long) (rand()&0xffff)) << 32) | 139 (((unsigned long) (rand()&0xffff)) << 48); 140 #endif 141 l1 += step; 142 l2 += step; 143 if ((w & 0xffff) == 0) 144 memtest_progress_step(w+iwords*off,words,'R'); 145 } 146 } 147 } 148 149 /* Like memtest_fill_random() but uses the two specified values to fill 150 * memory, in an alternated way (v1|v2|v1|v2|...) */ 151 void memtest_fill_value(unsigned long *l, size_t bytes, unsigned long v1, 152 unsigned long v2, char sym) 153 { 154 unsigned long step = 4096/sizeof(unsigned long); 155 unsigned long words = bytes/sizeof(unsigned long)/2; 156 unsigned long iwords = words/step; /* words per iteration */ 157 unsigned long off, w, *l1, *l2, v; 158 159 assert((bytes & 4095) == 0); 160 for (off = 0; off < step; off++) { 161 l1 = l+off; 162 l2 = l1+words; 163 v = (off & 1) ? v2 : v1; 164 for (w = 0; w < iwords; w++) { 165 #ifdef MEMTEST_32BIT 166 *l1 = *l2 = ((unsigned long) v) | 167 (((unsigned long) v) << 16); 168 #else 169 *l1 = *l2 = ((unsigned long) v) | 170 (((unsigned long) v) << 16) | 171 (((unsigned long) v) << 32) | 172 (((unsigned long) v) << 48); 173 #endif 174 l1 += step; 175 l2 += step; 176 if ((w & 0xffff) == 0) 177 memtest_progress_step(w+iwords*off,words,sym); 178 } 179 } 180 } 181 182 void memtest_compare(unsigned long *l, size_t bytes) { 183 unsigned long words = bytes/sizeof(unsigned long)/2; 184 unsigned long w, *l1, *l2; 185 186 assert((bytes & 4095) == 0); 187 l1 = l; 188 l2 = l1+words; 189 for (w = 0; w < words; w++) { 190 if (*l1 != *l2) { 191 printf("\n*** MEMORY ERROR DETECTED: %p != %p (%lu vs %lu)\n", 192 (void*)l1, (void*)l2, *l1, *l2); 193 exit(1); 194 } 195 l1 ++; 196 l2 ++; 197 if ((w & 0xffff) == 0) memtest_progress_step(w,words,'='); 198 } 199 } 200 201 void memtest_compare_times(unsigned long *m, size_t bytes, int pass, int times) { 202 int j; 203 204 for (j = 0; j < times; j++) { 205 memtest_progress_start("Compare",pass); 206 memtest_compare(m,bytes); 207 memtest_progress_end(); 208 } 209 } 210 211 void memtest_test(size_t megabytes, int passes) { 212 size_t bytes = megabytes*1024*1024; 213 unsigned long *m = malloc(bytes); 214 int pass = 0; 215 216 if (m == NULL) { 217 fprintf(stderr,"Unable to allocate %zu megabytes: %s", 218 megabytes, strerror(errno)); 219 exit(1); 220 } 221 while (pass != passes) { 222 pass++; 223 224 memtest_progress_start("Addressing test",pass); 225 memtest_addressing(m,bytes); 226 memtest_progress_end(); 227 228 memtest_progress_start("Random fill",pass); 229 memtest_fill_random(m,bytes); 230 memtest_progress_end(); 231 memtest_compare_times(m,bytes,pass,4); 232 233 memtest_progress_start("Solid fill",pass); 234 memtest_fill_value(m,bytes,0,(unsigned long)-1,'S'); 235 memtest_progress_end(); 236 memtest_compare_times(m,bytes,pass,4); 237 238 memtest_progress_start("Checkerboard fill",pass); 239 memtest_fill_value(m,bytes,ULONG_ONEZERO,ULONG_ZEROONE,'C'); 240 memtest_progress_end(); 241 memtest_compare_times(m,bytes,pass,4); 242 } 243 free(m); 244 } 245 246 void memtest_non_destructive_invert(void *addr, size_t size) { 247 volatile unsigned long *p = addr; 248 size_t words = size / sizeof(unsigned long); 249 size_t j; 250 251 /* Invert */ 252 for (j = 0; j < words; j++) 253 p[j] = ~p[j]; 254 } 255 256 void memtest_non_destructive_swap(void *addr, size_t size) { 257 volatile unsigned long *p = addr; 258 size_t words = size / sizeof(unsigned long); 259 size_t j; 260 261 /* Swap */ 262 for (j = 0; j < words; j += 2) { 263 unsigned long a, b; 264 265 a = p[j]; 266 b = p[j+1]; 267 p[j] = b; 268 p[j+1] = a; 269 } 270 } 271 272 void memtest(size_t megabytes, int passes) { 273 if (ioctl(1, TIOCGWINSZ, &ws) == -1) { 274 ws.ws_col = 80; 275 ws.ws_row = 20; 276 } 277 memtest_test(megabytes,passes); 278 printf("\nYour memory passed this test.\n"); 279 printf("Please if you are still in doubt use the following two tools:\n"); 280 printf("1) memtest86: http://www.memtest86.com/\n"); 281 printf("2) memtester: http://pyropus.ca/software/memtester/\n"); 282 exit(0); 283 } 284