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 #include <stdint.h>
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
memtest_progress_start(char * title,int pass)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
memtest_progress_end(void)78 void memtest_progress_end(void) {
79 printf("\x1b[H\x1b[2J"); /* Cursor home, clear screen. */
80 }
81
memtest_progress_step(size_t curr,size_t size,char c)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. */
memtest_addressing(unsigned long * l,size_t bytes,int interactive)93 int memtest_addressing(unsigned long *l, size_t bytes, int interactive) {
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 && interactive)
103 memtest_progress_step(j,words*2,'A');
104 }
105 /* Test */
106 p = l;
107 for (j = 0; j < words; j++) {
108 if (*p != (unsigned long)p) {
109 if (interactive) {
110 printf("\n*** MEMORY ADDRESSING ERROR: %p contains %lu\n",
111 (void*) p, *p);
112 exit(1);
113 }
114 return 1;
115 }
116 p++;
117 if ((j & 0xffff) == 0 && interactive)
118 memtest_progress_step(j+words,words*2,'A');
119 }
120 return 0;
121 }
122
123 /* Fill words stepping a single page at every write, so we continue to
124 * touch all the pages in the smallest amount of time reducing the
125 * effectiveness of caches, and making it hard for the OS to transfer
126 * pages on the swap.
127 *
128 * In this test we can't call rand() since the system may be completely
129 * unable to handle library calls, so we have to resort to our own
130 * PRNG that only uses local state. We use an xorshift* PRNG. */
131 #define xorshift64star_next() do { \
132 rseed ^= rseed >> 12; \
133 rseed ^= rseed << 25; \
134 rseed ^= rseed >> 27; \
135 rout = rseed * UINT64_C(2685821657736338717); \
136 } while(0)
137
memtest_fill_random(unsigned long * l,size_t bytes,int interactive)138 void memtest_fill_random(unsigned long *l, size_t bytes, int interactive) {
139 unsigned long step = 4096/sizeof(unsigned long);
140 unsigned long words = bytes/sizeof(unsigned long)/2;
141 unsigned long iwords = words/step; /* words per iteration */
142 unsigned long off, w, *l1, *l2;
143 uint64_t rseed = UINT64_C(0xd13133de9afdb566); /* Just a random seed. */
144 uint64_t rout = 0;
145
146 assert((bytes & 4095) == 0);
147 for (off = 0; off < step; off++) {
148 l1 = l+off;
149 l2 = l1+words;
150 for (w = 0; w < iwords; w++) {
151 xorshift64star_next();
152 *l1 = *l2 = (unsigned long) rout;
153 l1 += step;
154 l2 += step;
155 if ((w & 0xffff) == 0 && interactive)
156 memtest_progress_step(w+iwords*off,words,'R');
157 }
158 }
159 }
160
161 /* Like memtest_fill_random() but uses the two specified values to fill
162 * memory, in an alternated way (v1|v2|v1|v2|...) */
memtest_fill_value(unsigned long * l,size_t bytes,unsigned long v1,unsigned long v2,char sym,int interactive)163 void memtest_fill_value(unsigned long *l, size_t bytes, unsigned long v1,
164 unsigned long v2, char sym, int interactive)
165 {
166 unsigned long step = 4096/sizeof(unsigned long);
167 unsigned long words = bytes/sizeof(unsigned long)/2;
168 unsigned long iwords = words/step; /* words per iteration */
169 unsigned long off, w, *l1, *l2, v;
170
171 assert((bytes & 4095) == 0);
172 for (off = 0; off < step; off++) {
173 l1 = l+off;
174 l2 = l1+words;
175 v = (off & 1) ? v2 : v1;
176 for (w = 0; w < iwords; w++) {
177 #ifdef MEMTEST_32BIT
178 *l1 = *l2 = ((unsigned long) v) |
179 (((unsigned long) v) << 16);
180 #else
181 *l1 = *l2 = ((unsigned long) v) |
182 (((unsigned long) v) << 16) |
183 (((unsigned long) v) << 32) |
184 (((unsigned long) v) << 48);
185 #endif
186 l1 += step;
187 l2 += step;
188 if ((w & 0xffff) == 0 && interactive)
189 memtest_progress_step(w+iwords*off,words,sym);
190 }
191 }
192 }
193
memtest_compare(unsigned long * l,size_t bytes,int interactive)194 int memtest_compare(unsigned long *l, size_t bytes, int interactive) {
195 unsigned long words = bytes/sizeof(unsigned long)/2;
196 unsigned long w, *l1, *l2;
197
198 assert((bytes & 4095) == 0);
199 l1 = l;
200 l2 = l1+words;
201 for (w = 0; w < words; w++) {
202 if (*l1 != *l2) {
203 if (interactive) {
204 printf("\n*** MEMORY ERROR DETECTED: %p != %p (%lu vs %lu)\n",
205 (void*)l1, (void*)l2, *l1, *l2);
206 exit(1);
207 }
208 return 1;
209 }
210 l1 ++;
211 l2 ++;
212 if ((w & 0xffff) == 0 && interactive)
213 memtest_progress_step(w,words,'=');
214 }
215 return 0;
216 }
217
memtest_compare_times(unsigned long * m,size_t bytes,int pass,int times,int interactive)218 int memtest_compare_times(unsigned long *m, size_t bytes, int pass, int times,
219 int interactive)
220 {
221 int j;
222 int errors = 0;
223
224 for (j = 0; j < times; j++) {
225 if (interactive) memtest_progress_start("Compare",pass);
226 errors += memtest_compare(m,bytes,interactive);
227 if (interactive) memtest_progress_end();
228 }
229 return errors;
230 }
231
232 /* Test the specified memory. The number of bytes must be multiple of 4096.
233 * If interactive is true the program exists with an error and prints
234 * ASCII arts to show progresses. Instead when interactive is 0, it can
235 * be used as an API call, and returns 1 if memory errors were found or
236 * 0 if there were no errors detected. */
memtest_test(unsigned long * m,size_t bytes,int passes,int interactive)237 int memtest_test(unsigned long *m, size_t bytes, int passes, int interactive) {
238 int pass = 0;
239 int errors = 0;
240
241 while (pass != passes) {
242 pass++;
243
244 if (interactive) memtest_progress_start("Addressing test",pass);
245 errors += memtest_addressing(m,bytes,interactive);
246 if (interactive) memtest_progress_end();
247
248 if (interactive) memtest_progress_start("Random fill",pass);
249 memtest_fill_random(m,bytes,interactive);
250 if (interactive) memtest_progress_end();
251 errors += memtest_compare_times(m,bytes,pass,4,interactive);
252
253 if (interactive) memtest_progress_start("Solid fill",pass);
254 memtest_fill_value(m,bytes,0,(unsigned long)-1,'S',interactive);
255 if (interactive) memtest_progress_end();
256 errors += memtest_compare_times(m,bytes,pass,4,interactive);
257
258 if (interactive) memtest_progress_start("Checkerboard fill",pass);
259 memtest_fill_value(m,bytes,ULONG_ONEZERO,ULONG_ZEROONE,'C',interactive);
260 if (interactive) memtest_progress_end();
261 errors += memtest_compare_times(m,bytes,pass,4,interactive);
262 }
263 return errors;
264 }
265
266 /* A version of memtest_test() that tests memory in small pieces
267 * in order to restore the memory content at exit.
268 *
269 * One problem we have with this approach, is that the cache can avoid
270 * real memory accesses, and we can't test big chunks of memory at the
271 * same time, because we need to backup them on the stack (the allocator
272 * may not be usable or we may be already in an out of memory condition).
273 * So what we do is to try to trash the cache with useless memory accesses
274 * between the fill and compare cycles. */
275 #define MEMTEST_BACKUP_WORDS (1024*(1024/sizeof(long)))
276 /* Random accesses of MEMTEST_DECACHE_SIZE are performed at the start and
277 * end of the region between fill and compare cycles in order to trash
278 * the cache. */
279 #define MEMTEST_DECACHE_SIZE (1024*8)
memtest_preserving_test(unsigned long * m,size_t bytes,int passes)280 int memtest_preserving_test(unsigned long *m, size_t bytes, int passes) {
281 unsigned long backup[MEMTEST_BACKUP_WORDS];
282 unsigned long *p = m;
283 unsigned long *end = (unsigned long*) (((unsigned char*)m)+(bytes-MEMTEST_DECACHE_SIZE));
284 size_t left = bytes;
285 int errors = 0;
286
287 if (bytes & 4095) return 0; /* Can't test across 4k page boundaries. */
288 if (bytes < 4096*2) return 0; /* Can't test a single page. */
289
290 while(left) {
291 /* If we have to test a single final page, go back a single page
292 * so that we can test two pages, since the code can't test a single
293 * page but at least two. */
294 if (left == 4096) {
295 left += 4096;
296 p -= 4096/sizeof(unsigned long);
297 }
298
299 int pass = 0;
300 size_t len = (left > sizeof(backup)) ? sizeof(backup) : left;
301
302 /* Always test an even number of pages. */
303 if (len/4096 % 2) len -= 4096;
304
305 memcpy(backup,p,len); /* Backup. */
306 while(pass != passes) {
307 pass++;
308 errors += memtest_addressing(p,len,0);
309 memtest_fill_random(p,len,0);
310 if (bytes >= MEMTEST_DECACHE_SIZE) {
311 memtest_compare_times(m,MEMTEST_DECACHE_SIZE,pass,1,0);
312 memtest_compare_times(end,MEMTEST_DECACHE_SIZE,pass,1,0);
313 }
314 errors += memtest_compare_times(p,len,pass,4,0);
315 memtest_fill_value(p,len,0,(unsigned long)-1,'S',0);
316 if (bytes >= MEMTEST_DECACHE_SIZE) {
317 memtest_compare_times(m,MEMTEST_DECACHE_SIZE,pass,1,0);
318 memtest_compare_times(end,MEMTEST_DECACHE_SIZE,pass,1,0);
319 }
320 errors += memtest_compare_times(p,len,pass,4,0);
321 memtest_fill_value(p,len,ULONG_ONEZERO,ULONG_ZEROONE,'C',0);
322 if (bytes >= MEMTEST_DECACHE_SIZE) {
323 memtest_compare_times(m,MEMTEST_DECACHE_SIZE,pass,1,0);
324 memtest_compare_times(end,MEMTEST_DECACHE_SIZE,pass,1,0);
325 }
326 errors += memtest_compare_times(p,len,pass,4,0);
327 }
328 memcpy(p,backup,len); /* Restore. */
329 left -= len;
330 p += len/sizeof(unsigned long);
331 }
332 return errors;
333 }
334
335 /* Perform an interactive test allocating the specified number of megabytes. */
memtest_alloc_and_test(size_t megabytes,int passes)336 void memtest_alloc_and_test(size_t megabytes, int passes) {
337 size_t bytes = megabytes*1024*1024;
338 unsigned long *m = malloc(bytes);
339
340 if (m == NULL) {
341 fprintf(stderr,"Unable to allocate %zu megabytes: %s",
342 megabytes, strerror(errno));
343 exit(1);
344 }
345 memtest_test(m,bytes,passes,1);
346 free(m);
347 }
348
memtest(size_t megabytes,int passes)349 void memtest(size_t megabytes, int passes) {
350 if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
351 ws.ws_col = 80;
352 ws.ws_row = 20;
353 }
354 memtest_alloc_and_test(megabytes,passes);
355 printf("\nYour memory passed this test.\n");
356 printf("Please if you are still in doubt use the following two tools:\n");
357 printf("1) memtest86: http://www.memtest86.com/\n");
358 printf("2) memtester: http://pyropus.ca/software/memtester/\n");
359 exit(0);
360 }
361