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