xref: /redis-3.2.3/src/memtest.c (revision 22c9c407)
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <assert.h>
5 #include <limits.h>
6 #include <errno.h>
7 #include <termios.h>
8 #include <sys/ioctl.h>
9 
10 #if (ULONG_MAX == 4294967295UL)
11 #define MEMTEST_32BIT
12 #elif (ULONG_MAX == 18446744073709551615ULL)
13 #define MEMTEST_64BIT
14 #else
15 #error "ULONG_MAX value not supported."
16 #endif
17 
18 #ifdef MEMTEST_32BIT
19 #define ULONG_ONEZERO 0xaaaaaaaaaaaaaaaaUL
20 #define ULONG_ZEROONE 0x5555555555555555UL
21 #else
22 #define ULONG_ONEZERO 0xaaaaaaaaUL
23 #define ULONG_ZEROONE 0x55555555UL
24 #endif
25 
26 static struct winsize ws;
27 size_t progress_printed; /* Printed chars in screen-wide progress bar. */
28 size_t progress_full; /* How many chars to write to fill the progress bar. */
29 
30 void memtest_progress_start(char *title, int pass) {
31     int j;
32 
33     printf("\x1b[H\x1b[2J");    /* Cursor home, clear screen. */
34     /* Fill with dots. */
35     for (j = 0; j < ws.ws_col*(ws.ws_row-2); j++) printf(".");
36     printf("Please keep the test running several minutes per GB of memory.\n");
37     printf("Also check http://www.memtest86.com/ and http://pyropus.ca/software/memtester/");
38     printf("\x1b[H\x1b[2K");          /* Cursor home, clear current line.  */
39     printf("%s [%d]\n", title, pass); /* Print title. */
40     progress_printed = 0;
41     progress_full = ws.ws_col*(ws.ws_row-3);
42     fflush(stdout);
43 }
44 
45 void memtest_progress_end(void) {
46     printf("\x1b[H\x1b[2J");    /* Cursor home, clear screen. */
47 }
48 
49 void memtest_progress_step(size_t curr, size_t size, char c) {
50     size_t chars = (curr*progress_full)/size, j;
51 
52     for (j = 0; j < chars-progress_printed; j++) {
53         printf("%c",c);
54         progress_printed++;
55     }
56     fflush(stdout);
57 }
58 
59 /* Test that addressing is fine. Every location is populated with its own
60  * address, and finally verified. This test is very fast but may detect
61  * ASAP big issues with the memory subsystem. */
62 void memtest_addressing(unsigned long *l, size_t bytes) {
63     unsigned long words = bytes/sizeof(unsigned long);
64     unsigned long j, *p;
65 
66     /* Fill */
67     p = l;
68     for (j = 0; j < words; j++) {
69         *p = (unsigned long)p;
70         p++;
71         if ((j & 0xffff) == 0) memtest_progress_step(j,words*2,'A');
72     }
73     /* Test */
74     p = l;
75     for (j = 0; j < words; j++) {
76         if (*p != (unsigned long)p) {
77             printf("\n*** MEMORY ADDRESSING ERROR: %p contains %lu\n",
78                 (void*) p, *p);
79             exit(1);
80         }
81         p++;
82         if ((j & 0xffff) == 0) memtest_progress_step(j+words,words*2,'A');
83     }
84 }
85 
86 /* Fill words stepping a single page at every write, so we continue to
87  * touch all the pages in the smallest amount of time reducing the
88  * effectiveness of caches, and making it hard for the OS to transfer
89  * pages on the swap. */
90 void memtest_fill_random(unsigned long *l, size_t bytes) {
91     unsigned long step = 4096/sizeof(unsigned long);
92     unsigned long words = bytes/sizeof(unsigned long)/2;
93     unsigned long iwords = words/step;  /* words per iteration */
94     unsigned long off, w, *l1, *l2;
95 
96     assert((bytes & 4095) == 0);
97     for (off = 0; off < step; off++) {
98         l1 = l+off;
99         l2 = l1+words;
100         for (w = 0; w < iwords; w++) {
101 #ifdef MEMTEST_32BIT
102             *l1 = *l2 = ((unsigned long)     (rand()&0xffff)) |
103                         (((unsigned long)    (rand()&0xffff)) << 16);
104 #else
105             *l1 = *l2 = ((unsigned long)     (rand()&0xffff)) |
106                         (((unsigned long)    (rand()&0xffff)) << 16) |
107                         (((unsigned long)    (rand()&0xffff)) << 32) |
108                         (((unsigned long)    (rand()&0xffff)) << 48);
109 #endif
110             l1 += step;
111             l2 += step;
112             if ((w & 0xffff) == 0)
113                 memtest_progress_step(w+iwords*off,words,'R');
114         }
115     }
116 }
117 
118 /* Like memtest_fill_random() but uses the two specified values to fill
119  * memory, in an alternated way (v1|v2|v1|v2|...) */
120 void memtest_fill_value(unsigned long *l, size_t bytes, unsigned long v1,
121                         unsigned long v2, char sym)
122 {
123     unsigned long step = 4096/sizeof(unsigned long);
124     unsigned long words = bytes/sizeof(unsigned long)/2;
125     unsigned long iwords = words/step;  /* words per iteration */
126     unsigned long off, w, *l1, *l2, v;
127 
128     assert((bytes & 4095) == 0);
129     for (off = 0; off < step; off++) {
130         l1 = l+off;
131         l2 = l1+words;
132         v = (off & 1) ? v2 : v1;
133         for (w = 0; w < iwords; w++) {
134 #ifdef MEMTEST_32BIT
135             *l1 = *l2 = ((unsigned long)     (rand()&0xffff)) |
136                         (((unsigned long)    (rand()&0xffff)) << 16);
137 #else
138             *l1 = *l2 = ((unsigned long)     (rand()&0xffff)) |
139                         (((unsigned long)    (rand()&0xffff)) << 16) |
140                         (((unsigned long)    (rand()&0xffff)) << 32) |
141                         (((unsigned long)    (rand()&0xffff)) << 48);
142 #endif
143             l1 += step;
144             l2 += step;
145             if ((w & 0xffff) == 0)
146                 memtest_progress_step(w+iwords*off,words,sym);
147         }
148     }
149 }
150 
151 void memtest_compare(unsigned long *l, size_t bytes) {
152     unsigned long words = bytes/sizeof(unsigned long)/2;
153     unsigned long w, *l1, *l2;
154 
155     assert((bytes & 4095) == 0);
156     l1 = l;
157     l2 = l1+words;
158     for (w = 0; w < words; w++) {
159         if (*l1 != *l2) {
160             printf("\n*** MEMORY ERROR DETECTED: %p != %p (%lu vs %lu)\n",
161                 (void*)l1, (void*)l2, *l1, *l2);
162             exit(1);
163         }
164         l1 ++;
165         l2 ++;
166         if ((w & 0xffff) == 0) memtest_progress_step(w,words,'=');
167     }
168 }
169 
170 void memtest_compare_times(unsigned long *m, size_t bytes, int pass, int times) {
171     int j;
172 
173     for (j = 0; j < times; j++) {
174         memtest_progress_start("Compare",pass);
175         memtest_compare(m,bytes);
176         memtest_progress_end();
177     }
178 }
179 
180 void memtest_test(size_t megabytes, int passes) {
181     size_t bytes = megabytes*1024*1024;
182     unsigned long *m = malloc(bytes);
183     int pass = 0;
184 
185     if (m == NULL) {
186         fprintf(stderr,"Unable to allocate %zu megabytes: %s",
187             megabytes, strerror(errno));
188         exit(1);
189     }
190     while (pass != passes) {
191         pass++;
192 
193         memtest_progress_start("Addressing test",pass);
194         memtest_addressing(m,bytes);
195         memtest_progress_end();
196 
197         memtest_progress_start("Random fill",pass);
198         memtest_fill_random(m,bytes);
199         memtest_progress_end();
200         memtest_compare_times(m,bytes,pass,4);
201 
202         memtest_progress_start("Solid fill",pass);
203         memtest_fill_value(m,bytes,0,(unsigned long)-1,'S');
204         memtest_progress_end();
205         memtest_compare_times(m,bytes,pass,4);
206 
207         memtest_progress_start("Checkerboard fill",pass);
208         memtest_fill_value(m,bytes,ULONG_ONEZERO,ULONG_ZEROONE,'C');
209         memtest_progress_end();
210         memtest_compare_times(m,bytes,pass,4);
211     }
212 }
213 
214 void memtest(size_t megabytes, int passes) {
215     if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
216         ws.ws_col = 80;
217         ws.ws_row = 20;
218     }
219     memtest_test(megabytes,passes);
220     printf("\nYour memory passed this test.\n");
221     printf("Please if you are still in doubt use the following two tools:\n");
222     printf("1) memtest86: http://www.memtest86.com/\n");
223     printf("2) memtester: http://pyropus.ca/software/memtester/\n");
224     exit(0);
225 }
226