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