xref: /redis-3.2.3/src/memtest.c (revision d04afd62)
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